【问题标题】:Android: how to scroll back to RecyclerView item, after item is edited and re-saved?Android:如何在项目被编辑并重新保存后滚动回 RecyclerView 项目?
【发布时间】:2017-01-20 02:57:04
【问题描述】:

我有一个带有 CardViews 的 RecyclerView 列表,它工作正常。当用户在名为 ActActivity 的输入 UI 上输入数据时,会创建 CardView。当用户稍后单击 CardView 以编辑原始数据时,onItemClick 方法会使用 start 方法再次启动 ActActivity,该方法捕获适配器中正确的 CardView 项目位置。

当用户点击 UI 上的 Save 按钮保存编辑的数据时,ActActivity UI 关闭,RecyclerView 列表自动重新加载(MainActivity),更新后的 CardView 显示在 RecyclerView 列表中。我的问题是重新加载时的 RecyclerView 总是滚动到 CardView 项目列表的顶部。我希望列表返回到单击以进行编辑的原始 CardView。我不知何故需要在适配器中获取原始项目位置,并在重新创建时将其传递回 RecyclerView。我需要让 RecyclerView 知道该位置是针对刚刚编辑的 CardView 而不是新的 CardView。关于如何完成的任何想法?

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 输出:

RuntimeException:无法恢复活动 {com.example.jdw.v53/com.wimso.v053.MainActivity}:java.lang.NullPointerException 在 android.app.ActivityThread.performResumeActivity(ActivityThread.java:2575) 在 android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2603) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237) 在 android.os.Handler.dispatchMessage(Handler.java:99) 在 android.os.Looper.loop(Looper.java:137) 在 android.app.ActivityThread.main(ActivityThread.java:4745) 在 java.lang.reflect.Method.invokeNative(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:511) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 在 dalvik.system.NativeStart.main(本机方法) 引起:java.lang.NullPointerException 在 com.wimso.v053.MainActivity.onResume(MainActivity.java:370) 在 android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1184) 在 android.app.Activity.performResume(Activity.java:5082) 在 android.app.ActivityThread.performResumeActivity(ActivityThread.java:2565) 在 android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2603) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237) 在 android.os.Handler.dispatchMessage(Handler.java:99) 在 android.os.Looper.loop(Looper.java:137) 在 android.app.ActivityThread.main(ActivityThread.java:4745) 在 java.lang.reflect.Method.invokeNative(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:511) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 在 dalvik.system.NativeStart.main(本机方法)

【问题讨论】:

  • 你试过使用马厩ID吗?还有你是如何重新加载数据的?
  • 我见过一些 RecyclerView 示例使用稳定的 id,但我从未使用过。我会研究。我更新了上面的代码以显示数据是如何加载到 MainActivity 中的。

标签: android android-recyclerview android-adapter


【解决方案1】:

您可以使用context.startActivityForResult(intent,REQUEST_CODE); 启动ActActivity 类,如下所示

 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);
    }

ActActivity 完成数据保存后,您可以使用 setResult(intent) 将结果中的适配器位置设置回主 Activity

一旦你在MainActivity中得到onActivityResult()的Result,你就会知道你编辑后回来了,那么你就可以用这个方法滚动到该位置,如下图

    // 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);
    }  
}  

【讨论】:

  • 好吧,所以基本上用setResult让MainActivity知道我们已经从ActActivity返回,并没有创建一个新的MainActivity?另外,我确实想使用适配器位置滚动回该位置而不是某个偏移位置。
  • @AJW 是的,如果你不想偏移,请将其设置为 0
  • 好的。 REQUEST_CODE 有什么作用?我需要使用吗?
  • @AJW 给出任何整数值,并在 onActivityResult 中检查是否取回
  • 好的,那么 REQUEST_CODE 应该是正在编辑的项目的适配器位置吗?
【解决方案2】:

执行以下步骤 - 1. 跟踪用户单击编辑的卡片的适配器位置 (clickPosition)。这可以通过 MainActivity 中的状态变量来保存。

  1. 当 MainActivity 恢复时,检查它是否在卡片编辑后启动,如果是,则调用 api - recyclerView.scrollToPosition(*clickPosition*)

当然,这将适用于以下条件 - 1.您在ActActivity时没有同时修改recycler view数据集。 2. 如果是New Card或者Delete Card动作,可能处理方式会稍有不同

【讨论】:

  • 好的,我试试。我正在处理新卡和删除卡,所以都在那里。您能否提供一个状态变量的示例示例以及如何检查 MainActivity 在卡片编辑后是否重新启动与新启动?
  • 在您的 MainActivity 上创建一个类级变量并将其初始化为 -1。然后在导航到 ActActivity 时单击,将其设置为单击项目的适配器位置。当您导航回 MainActivity 时,检查类变量中的值(在您的 onResume 中,因为可能不会调用 onCreate),如果它具有非负值,请调用我提到的 api。调用 API 后不要忘记重置类变量
  • 好的会试试。我将类变量重置回-1,以重置状态,对吗?
  • 如何将变量设置为点击项的适配器位置?
  • 在您的onClick 方法中,您可以获得点击项目的位置。
【解决方案3】:

为此,第一步是当用户单击卡片时,将其位置保存在您的首选存储选项中,然后在重新加载RecyclerView 时,您可以平滑滚动到特定位置。为此,您需要创建一个自定义 LinearLayoutManager 。这就是你的做法:

    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;
        }
    }
}

然后将其分配给您的 RecyclerView 并调用 smoothScrollToPosition()

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

【讨论】:

  • 我很困惑。因此,如果我在 UI 上有 3 个 CardView,然后单击位于 UI 中间的 CardView #2,我希望用户在编辑和保存任何数据后立即返回 CardView #2。所以 RecyclerView 根本不需要滚动,只需将用户返回到 UI 的中间。我实际上是在试图阻止 RecyclerView 自动滚动到列表顶部。
猜你喜欢
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
  • 2021-12-28
  • 2019-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多