AJW
AJW

Reputation: 1649

Android: how to scroll back to RecyclerView item, after item is edited and re-saved?

I have a RecyclerView list with CardViews that is working fine. The CardViews are created when a user enters data on an input UI called ActActivity. When the user later clicks on a CardView to edit the original data, the onItemClick method launches ActActivity again using the start method that captures the correct CardView item position in the Adapter.

When the user clicks on a Save button on the UI to save the edited data, the ActActivity UI closes and the RecyclerView list is automatically re-loaded (MainActivity) and the updated CardView is shown in the RecyclerView list. My problem is the RecyclerView upon re-loading always scrolls to the top of the CardView item list. I would like the list to return to the original CardView that was clicked on to be edited. I somehow need to take the original item position in the Adapter and pass that back to the RecyclerView when it is re-created. And I need to let the RecyclerView know that the position is for a CardView that was just edited not a new CardView. Any ideas on how to accomplish?

MainActivity.java:

public class MainActivity extends AppCompatActivity
    ...
    void loadData(){
    SQLiteDB sqLiteDB = SQLiteDB.getInstance(this);

    List<Contact> contactList = new ArrayList<>();

    Cursor cursor = sqLiteDB.retrieve();
    Contact contact;
    cursor...
    Adapter.addAll(contactList);

    @Override
    public void onItemClick(int position, final View view) {
        cardview = (CardView) view;
        ActActivity.start(this, Adapter.getItem(position));
    }

ActActivity.java:

public class ActActivity extends AppCompatActivity {
    ...
    private Contact contact;

    public static void start(Context context){
        Intent intent = new Intent(context, ActActivity.class);
        context.startActivity(intent);
    }

    public static void start(Context context, Contact contact){
        Intent intent = new Intent(context, ActActivity.class);
        // From the OnItemClick method in the MainActivity the RecyclerView item
        // position from the Adapter is passed into a putExtra bundle that the
        // intent carries to this Activity.  The data is then copied in the onCreate()
        // below using getIntent().getParcelableExtra(). So this is to update an
        // existing CardView item.
        intent.putExtra(ActActivity.class.getSimpleName(), contact);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(layout.activ_act);

        // Fetch data from a parcelable object passed from the MainActivity.
        contact = getIntent().getParcelableExtra(ActActivity.class.getSimpleName());    
        ...
        // Update edited EditText lines to the data Model via contact
        contact.setDataInput(xEditText.getText().toString()); 
        // Update the data in the SQLite database.
        sqLiteDB.update(contact);
        ActActivity.this.finish();

Adapter.java:

public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<Contact> contactList;

    public Adapter(Context context) {
        this.context = context;
        this.contactList = new ArrayList<>();
    }

Logcat output:

RuntimeException: Unable to resume activity {com.example.jdw.v53/com.wimso.v053.MainActivity}: java.lang.NullPointerException at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2575) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2603) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.NullPointerException at com.wimso.v053.MainActivity.onResume(MainActivity.java:370) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1184) at android.app.Activity.performResume(Activity.java:5082) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2565) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2603)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237)  at android.os.Handler.dispatchMessage(Handler.java:99)  at android.os.Looper.loop(Looper.java:137)  at android.app.ActivityThread.main(ActivityThread.java:4745)  at java.lang.reflect.Method.invokeNative(Native Method)  at java.lang.reflect.Method.invoke(Method.java:511)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)  at dalvik.system.NativeStart.main(Native Method) 

Upvotes: 0

Views: 1088

Answers (3)

Hasif Seyd
Hasif Seyd

Reputation: 1694

You can start ActActivity class using context.startActivityForResult(intent,REQUEST_CODE); as shown below

 public static void start(Context context, Contact contact,int position){
        Intent intent = new Intent(context, ActActivity.class);
        // From the OnItemClick method in the MainActivity the RecyclerView item
        // position from the Adapter is passed into a putExtra bundle that the
        // intent carries to this Activity.  The data is then copied in the onCreate()
        // below using getIntent().getParcelableExtra(). So this is to update an
        // existing CardView item.
        intent.putExtra(ActActivity.class.getSimpleName(), contact);
        intent.putExtra("position", position);
        context.startActivityForResult(intent,2);
    }

once the ActActivity has finished saving the data, you can set the adapter position in the result back to main Activity using setResult(intent)

and once you get the Result in onActivityResult() in your MainActivity, you will come to know that you have come back after editing, so then you can use this method to scroll to the position ,as shown below

    // Call Back method  to get the Message form other Activity  
@Override  
protected void onActivityResult(int requestCode, int resultCode, Intent data)  
{  
    super.onActivityResult(requestCode, resultCode, data);  
    // check if the request code is same as what is passed  here it is 2  
    if(requestCode==2)  
    {  
        int position=data.getIntExtra("position", 0);  
        //do all your adapter resetting here for recyclerView
        linearLayoutManager.scrollToPositionWithOffset(position, 0);
    }  
}  

Upvotes: 1

OBX
OBX

Reputation: 6114

In order to do so, the first step is when the user click a card, save its position in your preferred storage option, then while reloading the RecyclerView you can smooth scroll to the particular position. To do so you'll need to create a custom LinearLayoutManager . This is how you do it:

    public class LinearLayoutManagerWithSmoothScroller extends LinearLayoutManager {

    public LinearLayoutManagerWithSmoothScroller(Context context) {
        super(context, VERTICAL, false);
    }

    public LinearLayoutManagerWithSmoothScroller(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class TopSnappedSmoothScroller extends LinearSmoothScroller {
        public TopSnappedSmoothScroller(Context context) {
            super(context);

        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return LinearLayoutManagerWithSmoothScroller.this
                    .computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}

Then assign this to your RecyclerView and invoke smoothScrollToPosition() .

 recyclerView.setLayoutManager(new LinearLayoutManagerWithSmoothScroller(context));
 recyclerView.smoothScrollToPosition(position);

Upvotes: 0

Dibzmania
Dibzmania

Reputation: 1994

Do the following steps - 1. Keep a track of the adapter position of the card (clickPosition) that the user clicked to edit. This you can keep via a state variable in the MainActivity.

  1. When the MainActivity is resumed check if it was launched after a card edit and if Yes, call the api - recyclerView.scrollToPosition(*clickPosition*)

Ofcourse, this will work pertaining to the following conditions - 1. The recycler view data set was not concurrently modified while you were in ActActivity. 2. If it is a New Card or Delete Card action, may be the handling would have to be done in a slightly different manner

Upvotes: 1

Related Questions