Не удается обновить RecyclerView из класса моей модели?

Я работаю над RecyclerView, который должен быть перетаскиваемым и перелистываемым. Все работает идеально.

Данные извлекаются в одном классе с именем ExerciseDataProvider, а код RV - это другой фрагмент RecyclerListViewFragment.

Проблема в том, что я не могу уведомить об изменении данных из метода FetchExercise on postExecute. Таким образом, данные не заполняются в RV.

Пожалуйста, направьте меня в правильном направлении.

АКТИВНОСТЬ

    public class DraggableSwipeableExampleActivity extends AppCompatActivity {
        private static final String FRAGMENT_TAG_DATA_PROVIDER = "data provider";
        private static final String FRAGMENT_LIST_VIEW = "list view";
        private static final String FRAGMENT_TAG_ITEM_PINNED_DIALOG = "item pinned dialog";

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_demo);

            if (savedInstanceState == null) {
                getSupportFragmentManager().beginTransaction()
                        .add(new ExampleDataProviderFragment(), FRAGMENT_TAG_DATA_PROVIDER)
                        .commit();
                getSupportFragmentManager().beginTransaction()
                        .add(R.id.container, new RecyclerListViewFragment(), FRAGMENT_LIST_VIEW)
                        .commit();
            }
        }

 public AbstractDataProvider getDataProvider() {
        final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DATA_PROVIDER);
        return ((ExampleDataProviderFragment) fragment).getDataProvider();
    }

ПОСТАВЩИК ДАННЫХ

public class ExerciseDataProvider extends AbstractDataProvider {
    private List<ConcreteData> mData;
    private ConcreteData mLastRemovedData;
    private int mLastRemovedPosition = -1;

    public ExerciseDataProvider() {
        new FetchExercise().execute();
        mData = new LinkedList<>();
    }

    class FetchExercise extends AsyncTask<Void,Void,Void> {

        @Override
        protected Void doInBackground(Void... params) {
            final int viewType = 0;
            final int swipeReaction = RecyclerViewSwipeManager.REACTION_CAN_SWIPE_UP | RecyclerViewSwipeManager.REACTION_CAN_SWIPE_DOWN;

            String url = "https://gist.githubusercontent.com/fake/cb9aa5494e7ee36ac3ca/raw/a4abfd19368063/exercise.JSON";
            Log.d("Path", url);
            try {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder().url(url).build();
                Response response = client.newCall(request).execute();
                String jsonData = response.body().string();
                try {
                    JSONArray jsonArray = new JSONArray(jsonData);
                    for (int i = 0; i < jsonArray.length(); i++) {
                        final long id = i;
                        JSONObject jsonObject = jsonArray.getJSONObject(i);
                        String exercise_name = jsonObject.getString("name");
                        int exercise_duration = jsonObject.getInt("duration");

                        mData.add(new ConcreteData(id, viewType, exercise_name, exercise_duration, swipeReaction));
                        Log.d("exercise_name", exercise_name);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
        }
    }


    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Data getItem(int index) {
        if (index < 0 || index >= getCount()) {
            throw new IndexOutOfBoundsException("index = " + index);
        }

        return mData.get(index);
    }

    @Override
    public int undoLastRemoval() {
        if (mLastRemovedData != null) {
            int insertedPosition;
            if (mLastRemovedPosition >= 0 && mLastRemovedPosition < mData.size()) {
                insertedPosition = mLastRemovedPosition;
            } else {
                insertedPosition = mData.size();
            }

            mData.add(insertedPosition, mLastRemovedData);

            mLastRemovedData = null;
            mLastRemovedPosition = -1;

            return insertedPosition;
        } else {
            return -1;
        }
    }

    @Override
    public void moveItem(int fromPosition, int toPosition) {
        if (fromPosition == toPosition) {
            return;
        }

        final ConcreteData item = mData.remove(fromPosition);
        mData.add(toPosition, item);
        mLastRemovedPosition = -1;
    }

    @Override
    public void removeItem(int position) {
        //noinspection UnnecessaryLocalVariable
        final ConcreteData removedItem = mData.remove(position);

        mLastRemovedData = removedItem;
        mLastRemovedPosition = position;
    }

    public static final class ConcreteData extends Data {

        private final long mId;
        private final String mText;
        private final int mViewType;
        private final int mDuration;
        private boolean mPinned;

        ConcreteData(long id, int viewType, String text, int duration, int swipeReaction) {
            mId = id;
            mViewType = viewType;
            mText = text;
            mDuration = duration;
        }

        @Override
        public int getViewType() {
            return mViewType;
        }

        @Override
        public int getDuration() {
            return mDuration;
        }

        @Override
        public long getId() {
            return mId;
        }

        @Override
        public String toString() {
            return mText;
        }

        @Override
        public String getText() {
            return mText;
        }

        @Override
        public boolean isPinned() {
            return mPinned;
        }

        @Override
        public void setPinned(boolean pinned) {
            mPinned = pinned;
        }

    }
}

RecyclerListViewFragment

public class RecyclerListViewFragment extends Fragment {
    private RecyclerView mRecyclerView;
    private RecyclerView.LayoutManager mLayoutManager;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.Adapter mWrappedAdapter;
    private RecyclerViewDragDropManager mRecyclerViewDragDropManager;
    private RecyclerViewSwipeManager mRecyclerViewSwipeManager;
    private RecyclerViewTouchActionGuardManager mRecyclerViewTouchActionGuardManager;

    public RecyclerListViewFragment() {
        super();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_recycler_list_view, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        //noinspection ConstantConditions
        mRecyclerView = (RecyclerView) getView().findViewById(R.id.recycler_view);
        mLayoutManager = new LinearLayoutManager(getContext());

        // touch guard manager  (this class is required to suppress scrolling while swipe-dismiss animation is running)
        mRecyclerViewTouchActionGuardManager = new RecyclerViewTouchActionGuardManager();
        mRecyclerViewTouchActionGuardManager.setInterceptVerticalScrollingWhileAnimationRunning(true);
        mRecyclerViewTouchActionGuardManager.setEnabled(true);

        // drag & drop manager
        mRecyclerViewDragDropManager = new RecyclerViewDragDropManager();
        mRecyclerViewDragDropManager.setDraggingItemShadowDrawable(
                (NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z3));

        // swipe manager
        mRecyclerViewSwipeManager = new RecyclerViewSwipeManager();

        //adapter
        final MyDraggableSwipeableItemAdapter myItemAdapter = new MyDraggableSwipeableItemAdapter(getDataProvider());
        myItemAdapter.setEventListener(new MyDraggableSwipeableItemAdapter.EventListener() {
            @Override
            public void onItemRemoved(int position) {
                ((DraggableSwipeableExampleActivity) getActivity()).onItemRemoved(position);
            }

            @Override
            public void onItemViewClicked(View v, boolean pinned) {
                onItemViewClick(v, pinned);
            }
        });

        mAdapter = myItemAdapter;

        mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(myItemAdapter);      // wrap for dragging
        mWrappedAdapter = mRecyclerViewSwipeManager.createWrappedAdapter(mWrappedAdapter);      // wrap for swiping

        final GeneralItemAnimator animator = new SwipeDismissItemAnimator();
        animator.setSupportsChangeAnimations(false);

        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mWrappedAdapter);  // requires *wrapped* adapter
        mRecyclerView.setItemAnimator(animator);

        // additional decorations
        //noinspection StatementWithEmptyBody
        if (supportsViewElevation()) {
            // Lollipop or later has native drop shadow feature. ItemShadowDecorator is not required.
        } else {
            mRecyclerView.addItemDecoration(new ItemShadowDecorator((NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z1)));
        }
        mRecyclerView.addItemDecoration(new SimpleListDividerDecorator(ContextCompat.getDrawable(getContext(), R.drawable.list_divider_h), true));
        mRecyclerViewTouchActionGuardManager.attachRecyclerView(mRecyclerView);
        mRecyclerViewSwipeManager.attachRecyclerView(mRecyclerView);
        mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView);
    }

    @Override
    public void onPause() {
        mRecyclerViewDragDropManager.cancelDrag();
        super.onPause();
    }

    @Override
    public void onDestroyView() {
        if (mRecyclerViewDragDropManager != null) {
            mRecyclerViewDragDropManager.release();
            mRecyclerViewDragDropManager = null;
        }

        if (mRecyclerViewSwipeManager != null) {
            mRecyclerViewSwipeManager.release();
            mRecyclerViewSwipeManager = null;
        }

        if (mRecyclerViewTouchActionGuardManager != null) {
            mRecyclerViewTouchActionGuardManager.release();
            mRecyclerViewTouchActionGuardManager = null;
        }

        if (mRecyclerView != null) {
            mRecyclerView.setItemAnimator(null);
            mRecyclerView.setAdapter(null);
            mRecyclerView = null;
        }

        if (mWrappedAdapter != null) {
            WrapperAdapterUtils.releaseAll(mWrappedAdapter);
            mWrappedAdapter = null;
        }
        mAdapter = null;
        mLayoutManager = null;

        super.onDestroyView();
    }

    private void onItemViewClick(View v, boolean pinned) {
        int position = mRecyclerView.getChildAdapterPosition(v);
        if (position != RecyclerView.NO_POSITION) {
            ((DraggableSwipeableExampleActivity) getActivity()).onItemClicked(position);
        }
    }

    public AbstractDataProvider getDataProvider() {
        return ((DraggableSwipeableExampleActivity) getActivity()).getDataProvider();
    }

    public void notifyItemChanged(int position) {
        mAdapter.notifyItemChanged(position);
    }

    public void notifyItemInserted(int position) {
        mAdapter.notifyItemInserted(position);
        mRecyclerView.scrollToPosition(position);
    }
}

person user3467240    schedule 12.03.2016    source источник
comment
вы не обновляете пользовательский интерфейс в OnPostExecute   -  person Veeresh Charantimath    schedule 12.03.2016
comment
установите адаптер в OnPostExecute   -  person Veeresh Charantimath    schedule 12.03.2016
comment
@VeereshCharantimath Как установить адаптер в файле OnPostExecute. Покажи мне код.   -  person user3467240    schedule 12.03.2016


Ответы (2)


Чтобы обновить recyclerView из onPostExecute в классе поставщика данных, ваш onPostExecute должен иметь доступ к context, где определен ваш recyclerView.

Поскольку ваша FetchExercise асинхронная задача определена внутри класса ExerciseDataProvider, попробуйте передать activity context конструктору ExerciseDataProvider, а затем передать его FetchExercise асинхронной задаче, как описано здесь: получение контекста в AsyncTask

public class MyCustomTask extends AsyncTask<Void, Void, Long> {
    private Context mContext;
        public MyCustomTask (Context context){
           mContext = context;
        }
        protected void onPostExecute(Long result) {
           //use mContext to update recycler view
        }
    }
}

Используйте context для обновления файла recyclerView.


ОБНОВЛЕНИЕ

Шаг 1

Определите interface, который будет уведомлять ваш activity об изменении набора данных внутри класса, который инициализирует ваш data provider class, и передайте activity context конструктору data provider class.

public class ExampleDataProviderFragment extends Fragment {
    private AbstractDataProvider mDataProvider;

    //Define an interface that will notify your activity of data set change
    public interface EventListener {
        void onNotifyDataSetChanged();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setRetainInstance(true); 

        //Pass activity context to ExerciseDataProvider
        mDataProvider = new ExerciseDataProvider(getActivity());
    }

    public AbstractDataProvider getDataProvider() {
        return mDataProvider;
    }
}

Шаг 2

Добавьте параметр context в конструктор ExerciseDataProvider и используйте его для уведомления об активности, реализующей ваш интерфейс, для уведомления об изменении набора данных.

public class ExerciseDataProvider extends AbstractDataProvider {
    private List<ConcreteData> mData;
    private ConcreteData mLastRemovedData;
    private int mLastRemovedPosition = -1;

    //Add context parameter to constructor
    public ExerciseDataProvider(Context context) {

        //Pass context to async task

        new FetchExercise(context).execute();
        mData = new LinkedList<>();
    }

    class FetchExercise extends AsyncTask<Void,Void,Integer> {
        Context mContext;

        public FetchExercise(Context context) {
            mContext = context;
        }

        @Override
        protected Integer doInBackground(Void... params) {
            ...
            return 1;
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            //Typecast context to interface defined above 
            //and notify dataset changes by calling its method

            ExampleDataProviderFragment.EventListener eventListener = (ExampleDataProviderFragment.EventListener)mContext;
            eventListener.onNotifyDataSetChanged();

        }
    }
}

Шаг 3

Реализуйте вышеописанное interface в своем activity class и уведомите recyclerview adapter внутри него.

public class DraggableSwipeableExampleActivity extends AppCompatActivity 
    implements ExampleDataProviderFragment.EventListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }

    //implement interface method and notify recyclerview of changes
    @Override
    public void onNotifyDataSetChanged() {
        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_LIST_VIEW);

        // you might need to change visibility of `mWrappedAdapter` in the fragment that defines it or create a getter for it so that you can access it here
        ((RecyclerListViewFragment) fragment).mWrappedAdapter.notifyDataSetChanged(); 

    }
 ...
}
person random    schedule 14.03.2016
comment
при передаче контекста я получаю много ошибок. Можете ли вы просто показать это в моем коде, пожалуйста. - person user3467240; 14.03.2016
comment
Большое спасибо. +1 и принято - person user3467240; 15.03.2016

Я думаю, что @random верен, вы должны уведомлять о своем представлении Recycle после выполнения.

@Override
protected void onPostExecute(Void aVoid) {
    mRecyclerViewAdapter.notifyDataSetChanged();
    super.onPostExecute(aVoid);
}

или если вы что-то сделали в своей асинхронной задаче, чтобы добавить/удалить что-то в наборе данных, вы бы сделали:

@Override
protected void onPostExecute(Void aVoid) {
    mRecyclerViewAdapter.notifyItemRemoved(itemposition); // or item added
    mRecyclerViewAdapter.notifyDataSetChanged();
    super.onPostExecute(aVoid);
}

Надеюсь, поможет !

person Doug Ray    schedule 14.03.2016