2016-01-17 9 views
6

अद्यतन करने के लिए उपयोग करने के लिए किस डिज़ाइन अवधारणा का उपयोग मैं एक ऐप पर काम कर रहा हूं जो एक समय रेखा पर एक कार्यसूची निर्धारित करता है।यूआई async

enter image description here

डेटा एक SQLite DB में संग्रहीत है:

इस एप्लिकेशन कैसे पल में बनाया गया है का एक मोटा लेआउट है। जब Timeline (एक सिंगलटन ऑब्जेक्ट) डेटाबेस सहायक वर्ग से डेटा का अनुरोध करता है, तो उसे Event एस का एक ऐरेलिस्ट प्राप्त होता है (उदाहरण के लिए Event 1 मई 2016 को 03:00 बजे से शुरू होने वाला कर्तव्य हो सकता है और मई के तीसरे स्थान पर समाप्त हो सकता है 2016 16:00 बजे)। Timeline फिर इन Event एस से TimelineItem एस को बदलता है, एक वर्ग किसी विशेष दिन के लिए Event का प्रतिनिधित्व करता है।

Event एस की लोडिंग और Event एस से TimelineItem दोनों का रूपांतरण AsyncTasks में किया जाता है। अब तक सब ठीक है।

अब वह हिस्सा आता है जिसके साथ मैं संघर्ष कर रहा हूं: एक नए डीबी लाने के बाद यूआई को अपडेट करना।

मेरा पहला दृष्टिकोण RecyclerView एडाप्टर के टाइमलाइन आइटम्स के अद्यतन ऐरेलिस्ट को पास करना था और एडाप्टर को पता था कि notifyDatasetChanged() के साथ डेटा बदल गया है। इस दृष्टिकोण के साथ समस्या यह है कि 1) बहुत सारे अनावश्यक काम किए जा रहे हैं (क्योंकि हम सभी घटनाओं/टाइमलाइन इटम्स का पुनर्मूल्यांकन कर रहे हैं, न केवल बदले गए हैं) और 2) RecyclerView पर स्क्रॉल स्थिति प्रत्येक डीबी के बाद रीसेट हो गई है fetch

मेरे दूसरे दृष्टिकोण में, मैंने यह जांचने के लिए कुछ विधियों को लागू किया है कि अंतिम समय के बाद से कौन सा ईवेंट/टाइमलाइन इटम्स बदल गया है, केवल notifyItemChanged() के साथ उन टाइमलाइन आइटम्स को बदलने के विचार के साथ। कम काम किया जा रहा है और स्क्रॉल पदों के बारे में चिंता करने की कोई ज़रूरत नहीं है। मुश्किल सा है कि जाँच के जो आइटम के लिए कुछ समय ले करता है बदल गया है है, इसलिए यह रूप में अच्छी तरह async किया जाना चाहिए:

मैं onProgressUpdate() में बस घटनाओं ओटो पोस्टिंग द्वारा doInBackground() में कोड जोड़तोड़ और यूआई अद्यतन करने करने की कोशिश की।

private class InsertEventsTask extends AsyncTask<Void, Integer, Void> { 

    @Override 
    protected Void doInBackground(Void... params) { 
     ArrayList<Event> events = mCachedEvents; 

     // if mChangedEvents is not null and not empty 
     if (events != null && !events.isEmpty()) { 
      // get the list of pairs for the events 
      ArrayList<TimelineItemForDateTimePair> listOfPairs = convertEventsToPairs(events); 
      // insert the TimelineItems from the pairs into the Timeline 
      for (int i = 0; i < listOfPairs.size(); i++) { 
       // get the last position for the DateTime associated with the pair 
       int position = findLastPositionForDate(listOfPairs.get(i).dateTime); 
       // if position is -1, the events started on a day before the timeline starts 
       // so keep skipping pairs until position > -1 
       if (position > -1) { 
        // if the item is a PlaceholderItem 
        if (mTimelineItems.get(position).isPlaceholderItem) { 
         // remove the PlaceholderItem 
         mTimelineItems.remove(position); 
         // and add the TimelineItem from the pair at the position the PlaceholderItem was at 
         mTimelineItems.add(position, listOfPairs.get(i).timelineItem); 
         // update the UI on the UI thread 
         publishProgress(position, TYPE_CHANGED); 
        } else { // if the item is not a PlaceholderItem, there's already an normal TimelineItem in place 
         // place the new item at the next position on the Timeline 
         mTimelineItems.add(position + 1, listOfPairs.get(i).timelineItem); 
         publishProgress(position, TYPE_ADDED); 
        } 
       } 
      } 
     } 
     return null; 
    } 

    /** 
    * onProgressUpdate handles the UI changes on the UI thread for us. Type int available: 
    * - TYPE_CHANGED 
    * - TYPE_ADDED 
    * - TYPE_DELETED 
    * 
    * @param values value[0] is the position as <code>int</code>, 
    *    value[1] is the type of manipulation as <code>int</code> 
    */ 
    @Override 
    protected void onProgressUpdate(Integer... values) { 
     int position = values[0]; 
     int type = values[1]; 

     // update the UI for each changed/added/deleted TimelineItem 
     if (type == TYPE_CHANGED) { 
      BusProvider.getInstance().post(new TimelineItemChangedNotification(position)); 
     } else if (type == TYPE_ADDED) { 
      BusProvider.getInstance().post((new TimelineItemAddedNotification(position))); 
     } else if (type == TYPE_DELETED) { 
      // TODO: make delete work bro! 
     } 
    } 
} 

समस्या है, कि किसी भी तरह है, जबकि इस प्रगति पूरी तरह तैनात किया जा रहा है भोजनालयों यूआई स्क्रॉल।

मेरी मुख्य समस्या यह है: जब मैं एडाप्टर के डेटा सेट (टाइमलाइनइटम) में एक विशिष्ट आइटम अपडेट करता हूं, तो सूचित करें ItemChanged() आइटम को बदलता है लेकिन आइटम को सही स्थिति में नहीं डालता है।

यहाँ मेरी एडाप्टर है:

/** 
* A custom RecyclerView Adapter to display a Timeline in a TimelineFragment. 
*/ 
public class TimelineAdapter extends RecyclerView.Adapter<TimelineAdapter.TimelineItemViewHolder> { 

/************* 
* VARIABLES * 
*************/ 

private ArrayList<TimelineItem> mTimelineItems; 

/**************** 
* CONSTRUCTORS * 
****************/ 

/** 
* Constructor with <code>ArrayList<TimelineItem></code> as data set argument. 
* 
* @param timelineItems ArrayList with TimelineItems to display 
*/ 
public TimelineAdapter(ArrayList<TimelineItem> timelineItems) { 
    this.mTimelineItems = timelineItems; 
} 

// Create new views (invoked by the layout manager) 
@Override 
public TimelineItemViewHolder onCreateViewHolder(ViewGroup parent, 
               int viewType) { 
    // create a new view 
    View v = LayoutInflater.from(parent.getContext()) 
      .inflate(R.layout.item_timeline, parent, false); 
    // set the view's size, margins, paddings and layout parameters 
    // ... 

    return new TimelineItemViewHolder(v); 
} 

// Replace the contents of a view (invoked by the layout manager) 
@Override 
public void onBindViewHolder(TimelineItemViewHolder holder, int position) { 
    // - get element from your data set at this position 
    // - replace the contents of the view with that element 

    // if the item is a ShowPreviousMonthsItem, set the showPreviousMonthsText accordingly 
    if (mTimelineItems.get(position).isShowPreviousMonthsItem) { 
     holder.showPreviousMonthsText.setText(mTimelineItems.get(position).showPreviousMonthsText); 
    } else { // otherwise set the showPreviousMonthsText blank 
     holder.showPreviousMonthsText.setText(""); 
    } 

    // day of month & day of week of the TimelineItem 
    if (mTimelineItems.get(position).isFirstItemOfDay) { 
     holder.dayOfWeek.setText(mTimelineItems.get(position).dayOfWeek); 
     holder.dayOfMonth.setText(mTimelineItems.get(position).dayOfMonth); 
    } else { 
     holder.dayOfWeek.setText(""); 
     holder.dayOfMonth.setText(""); 
    } 

    // Event name for the TimelineItem 
    holder.name.setText(mTimelineItems.get(position).name); 

    // place and goingTo of the TimelineItem 

    // if combinedPlace == "" 
    if(mTimelineItems.get(position).combinedPlace.equals("")) { 
     if (mTimelineItems.get(position).isFirstDayOfEvent) { 
      holder.place.setText(mTimelineItems.get(position).place); 
     } else { 
      holder.place.setText(""); 
     } 
     if (mTimelineItems.get(position).isLastDayOfEvent) { 
      holder.goingTo.setText(mTimelineItems.get(position).goingTo); 
     } else { 
      holder.goingTo.setText(""); 
     } 
     holder.combinedPlace.setText(""); 
    } else { 
     holder.place.setText(""); 
     holder.goingTo.setText(""); 
     holder.combinedPlace.setText(mTimelineItems.get(position).combinedPlace); 
    } 

    if(mTimelineItems.get(position).startDateTime != null) { 
     holder.startTime.setText(mTimelineItems.get(position).startDateTime.toString("HH:mm")); 
    } else { 
     holder.startTime.setText(""); 
    } 

    if(mTimelineItems.get(position).endDateTime != null) { 
     holder.endTime.setText(mTimelineItems.get(position).endDateTime.toString("HH:mm")); 
    } else { 
     holder.endTime.setText(""); 
    } 


    if (!mTimelineItems.get(position).isShowPreviousMonthsItem) { 
     if (mTimelineItems.get(position).date.getDayOfWeek() == DateTimeConstants.SUNDAY) { 
      holder.dayOfWeek.setTextColor(Color.RED); 
      holder.dayOfMonth.setTextColor(Color.RED); 
     } else { 
      holder.dayOfWeek.setTypeface(null, Typeface.NORMAL); 
      holder.dayOfMonth.setTypeface(null, Typeface.NORMAL); 
      holder.dayOfWeek.setTextColor(Color.GRAY); 
      holder.dayOfMonth.setTextColor(Color.GRAY); 
     } 
    } else { 
     ((RelativeLayout) holder.dayOfWeek.getParent()).setBackgroundColor(Color.WHITE); 
    } 

    holder.bindTimelineItem(mTimelineItems.get(position)); 
} 

// Return the size of the data set (invoked by the layout manager) 
@Override 
public int getItemCount() { 
    return mTimelineItems.size(); 
} 

// replace the data set 
public void setTimelineItems(ArrayList<TimelineItem> timelineItems) { 
    this.mTimelineItems = timelineItems; 
} 

// replace an item in the data set 
public void swapTimelineItemAtPosition(TimelineItem item, int position) { 
    mTimelineItems.remove(position); 
    mTimelineItems.add(position, item); 
    notifyItemChanged(position); 
} 

// the ViewHolder class containing the relevant views, 
// also binds the Timeline item itself to handle onClick events 
public class TimelineItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
    protected TextView dayOfWeek; 
    protected TextView dayOfMonth; 
    protected TextView showPreviousMonthsText; 
    protected TextView name; 
    protected TextView place; 
    protected TextView combinedPlace; 
    protected TextView goingTo; 
    protected TextView startTime; 
    protected TextView endTime; 

    protected TimelineItem timelineItem; 

    public TimelineItemViewHolder(View view) { 
     super(view); 
     view.setOnClickListener(this); 
     this.dayOfWeek = (TextView) view.findViewById(R.id.day_of_week); 
     this.dayOfMonth = (TextView) view.findViewById(R.id.day_of_month); 
     this.showPreviousMonthsText = (TextView) view.findViewById(R.id.load_previous_data); 
     this.name = (TextView) view.findViewById(R.id.name); 
     this.place = (TextView) view.findViewById(R.id.place); 
     this.combinedPlace = (TextView) view.findViewById(R.id.combined_place); 
     this.goingTo = (TextView) view.findViewById(R.id.going_to); 
     this.startTime = (TextView) view.findViewById(R.id.start_time); 
     this.endTime = (TextView) view.findViewById(R.id.end_time); 
    } 

    public void bindTimelineItem(TimelineItem item) { 
     timelineItem = item; 
    } 

    // handles the onClick of a TimelineItem 
    @Override 
    public void onClick(View v) { 
     // if the TimelineItem is a ShowPreviousMonthsItem 
     if (timelineItem.isShowPreviousMonthsItem) { 
      BusProvider.getInstance().post(new ShowPreviousMonthsRequest()); 
     } 
     // if the TimelineItem is a PlaceholderItem 
     else if (timelineItem.isPlaceholderItem) { 
      Toast.makeText(v.getContext(), "(no details)", Toast.LENGTH_SHORT).show(); 
     } 
     // else the TimelineItem is an actual event 
     else { 
      Toast.makeText(v.getContext(), "eventId = " + timelineItem.eventId, Toast.LENGTH_SHORT).show(); 
     } 
    } 
} 

और इस विधि कि TimelineFragment में शुरू हो रहा है जब एक परिवर्तन घटना बस में पोस्ट किया जाता है है:

@Subscribe 
public void onTimelineItemChanged(TimelineItemChangedNotification notification) { 
    int position = notification.position; 
    Log.d(TAG, "TimelineItemChanged detected for position " + position); 
    mAdapter.swapTimelineItemAtPosition(mTimeline.mTimelineItems.get(position), position); 
    mAdapter.notifyItemChanged(position); 
    Log.d(TAG, "Item for position " + position + " swapped"); 
} 

नोट करने के लिए एक बात यह है कि है एडाप्टर का डेटा सेट पर्याप्त रूप से परिवर्तित डेटा से दूर स्क्रॉल करने के बाद ठीक से प्रदर्शित होता है और इसके बाद स्थिति में वापस आ जाता है। प्रारंभ में यूआई पूरी तरह से गड़बड़ है हालांकि।

संपादित करें:

मैंने पाया कि

mAdapter.notifyItemRangeChanged(position, mAdapter.getItemCount()); 

जोड़ने समस्या का समाधान होता है, लेकिन - दुर्भाग्य से - सेट एक करने के लिए स्क्रॉल स्थिति बदल किया जा रहा :(

यहाँ मेरी TimelineFragment है:

/** 
* Fragment displaying a Timeline using a RecyclerView 
*/ 
public class TimelineFragment extends BackHandledFragment { 
    // DEBUG flag and TAG 
    private static final boolean DEBUG = false; 
    private static final String TAG = TimelineFragment.class.getSimpleName(); 

// variables 
protected RecyclerView mRecyclerView; 
protected TimelineAdapter mAdapter; 
protected LinearLayoutManager mLinearLayoutManager; 
protected Timeline mTimeline; 
protected MenuItem mMenuItemScroll2Today; 
protected MenuItem mMenuItemReload; 
protected String mToolbarTitle; 
// TODO: get the value of this boolean from the shared preferences 
private boolean mUseTimelineItemDividers = true; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    // get a handle to the app's Timeline singleton 
    mTimeline = Timeline.getInstance(); 
    setHasOptionsMenu(true); 
} 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
         Bundle savedInstanceState) { 
    View rootView = inflater.inflate(R.layout.fragment_timeline, container, false); 
    rootView.setTag(TAG); 

    mRecyclerView = (RecyclerView) rootView.findViewById(R.id.timeline_list); 
    mRecyclerView.hasFixedSize(); 

    // LinearLayoutManager constructor 
    mLinearLayoutManager = new LinearLayoutManager(getActivity()); 
    // set the layout manager 
    setRecyclerViewLayoutManager(); 
    // adapter constructor 
    mAdapter = new TimelineAdapter(mTimeline.mTimelineItems); 
    // set the adapter for the RecyclerView. 
    mRecyclerView.setAdapter(mAdapter); 

    // add lines between the different items if using them 
    if (mUseTimelineItemDividers) { 
     RecyclerView.ItemDecoration itemDecoration = 
       new TimelineItemDivider(this.getContext()); 
     mRecyclerView.addItemDecoration(itemDecoration); 
    } 

    // add the onScrollListener 
    mRecyclerView.addOnScrollListener(new TimelineOnScrollListener(mLinearLayoutManager) { 
     // when the first visible item on the Timeline changes, 
     // adjust the Toolbar title accordingly 
     @Override 
     public void onFirstVisibleItemChanged(int position) { 
      mTimeline.mCurrentScrollPosition = position; 
      try { 
       String title = mTimeline.mTimelineItems 
         .get(position).date 
         .toString(TimelineConfig.TOOLBAR_DATE_FORMAT); 
       // if mToolbarTitle is null, set it to the new title and post on bus 
       if (mToolbarTitle == null) { 
        if (DEBUG) 
         Log.d(TAG, "mToolbarTitle is null - posting new title request on bus: " + title); 
        mToolbarTitle = title; 
        BusProvider.getInstance().post(new ChangeToolbarTitleRequest(mToolbarTitle)); 
       } else { // if mToolbarTitle is not null 
        // only post on the bus if the new title is different from the previous one 
        if (!title.equals(mToolbarTitle)) { 
         if (DEBUG) 
          Log.d(TAG, "mToolbarTitle is NOT null, but new title detected - posting new title request on bus: " + title); 
         mToolbarTitle = title; 
         BusProvider.getInstance().post(new ChangeToolbarTitleRequest(mToolbarTitle)); 
        } 
       } 

      } catch (NullPointerException e) { 
       // if the onFirstVisibleItemChanged is called on a "ShowPreviousMonthsItem", 
       // leave the title as it is 
      } 
     } 
    }); 

    return rootView; 
} 

/** 
* Set RecyclerView's LayoutManager to the one given. 
*/ 
public void setRecyclerViewLayoutManager() { 
    int scrollPosition; 

    // If a layout manager has already been set, get current scroll position. 
    if (mRecyclerView.getLayoutManager() != null) { 
     scrollPosition = ((LinearLayoutManager) mRecyclerView.getLayoutManager()) 
       .findFirstCompletelyVisibleItemPosition(); 
    } else { 
     scrollPosition = mTimeline.mFirstPositionForToday; 
    } 

    mRecyclerView.setLayoutManager(mLinearLayoutManager); 
    mLinearLayoutManager.scrollToPositionWithOffset(scrollPosition, 0); 
} 

// set additional menu items for the Timeline fragment 
@Override 
public void onPrepareOptionsMenu(Menu menu) { 
    // scroll to today 
    mMenuItemScroll2Today = menu.findItem(R.id.action_scroll2today); 
    mMenuItemScroll2Today.setVisible(true); 
    mMenuItemScroll2Today.setIcon(Timeline.getIconForDateTime(new DateTime())); 
    mMenuItemScroll2Today.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 
     @Override 
     public boolean onMenuItemClick(MenuItem item) { 
      // stop scrolling 
      mRecyclerView.stopScroll(); 
      // get today's position 
      int todaysPosition = mTimeline.mFirstPositionForToday; 
      // scroll to today's position 
      mLinearLayoutManager.scrollToPositionWithOffset(todaysPosition, 0); 
      return false; 
     } 
    }); 

    // reload data from Hacklberry 
    mMenuItemReload = menu.findItem(R.id.action_reload_from_hacklberry); 
    mMenuItemReload.setVisible(true); 
    mMenuItemReload.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 
     @Override 
     public boolean onMenuItemClick(MenuItem item) { 
      // stop scrolling 
      mRecyclerView.stopScroll(); 
      // 
      mTimeline.reloadDBForCurrentMonth(); 
      mTimeline.loadEventsFromUninfinityDBAsync(mTimeline.mTimelineStart, mTimeline.mTimelineEnd); 
      return false; 
     } 
    }); 

    super.onPrepareOptionsMenu(menu); 
} 

@Override 
public void onResume() { 
    super.onResume(); 
    // if the Timeline has been invalidated, let AllInOneActivity know it needs to replace 
    // this Fragment with a new one 
    if (mTimeline.isInvalidated()) { 
     Log.d(TAG, "posting TimelineInvalidatedNotification on the bus ..."); 
     BusProvider.getInstance().post(
       new TimelineInvalidatedNotification()); 
    } 
    // fetch today's menu icon 
    if (mMenuItemScroll2Today != null) { 
     if (DEBUG) Log.d(TAG, "fetching scroll2today menu icon"); 
     mMenuItemScroll2Today.setIcon(Timeline.getIconForDateTime(new DateTime())); 
    } 
} 

// from BackHandledFragment 
@Override 
public String getTagText() { 
    return TAG; 
} 

// from BackHandledFragment 
@Override 
public boolean onBackPressed() { 
    return false; 
} 

@Subscribe 
public void onHacklberryReloaded(HacklberryLoadedNotification notification) { 
    resetReloading(); 
} 

// handles ShowPreviousMonthsRequests posted on the bus by the TimelineAdapter's ShowPreviousMonthsItem onClick() 
@Subscribe 
public void onShowPreviousMonthsRequest(ShowPreviousMonthsRequest request) { 
    // create an empty OnItemTouchListener to prevent the user from manipulating 
    // the RecyclerView while it loads more data (would mess up the scroll position) 
    EmptyOnItemTouchListener listener = new EmptyOnItemTouchListener(); 
    // add it to the RecyclerView 
    mRecyclerView.addOnItemTouchListener(listener); 
    // load the previous months (= add the required TimelineItems) 
    int newScrollToPosition = mTimeline.showPreviousMonths(); 
    // pass the new data set to the TimelineAdapter 
    mAdapter.setTimelineItems(mTimeline.mTimelineItems); 
    // notify the adapter the data set has changed 
    mAdapter.notifyDataSetChanged(); 
    // scroll to the last scroll (updated) position 
    mLinearLayoutManager.scrollToPositionWithOffset(newScrollToPosition, 0); 
} 

@Subscribe 
public void onTimelineItemChanged(TimelineItemChangeNotification notification) { 
    int position = notification.position; 
    Log.d(TAG, "TimelineItemChanged detected for position " + position); 
    mAdapter.swapTimelineItemAtPosition(mTimeline.mTimelineItems.get(position), position); 
    //mAdapter.notifyItemRangeChanged(position, position); 
    Log.d(TAG, "Item for position " + position + " swapped"); 
} 

मैंने पहले लोड होने के बाद ऐप का एक स्क्रीनशॉट लिया है। I'l एल असली त्वरित initialisation पर क्या होता है समझाने:

  1. समय सभी दिन PlaceholderItems साथ (सिर्फ एक तिथि के साथ एक TimelineItem) को आबाद करने से बनाया गया है।
  2. घटनाक्रम डीबी से भरी हुई और TimelineItems
  3. जब भी कोई नया TimelineItem बदल गया है करने के लिए बदल दिया और तैयार है कर रहे हैं, समय नए के साथ उस विशेष स्थिति के लिए एडाप्टर के डेटा सेट अद्यतन करने के लिए ओटो बस के माध्यम से TimelineFragment pokes TimelineItem।

यहाँ प्रारंभिक लोड के बाद होता है की एक स्क्रीनशॉट:

समय भरी हुई है लेकिन कुछ वस्तुओं गलत स्थिति में डाला जाता है।

enter image description here

जब दूर स्क्रॉल और दिनों की सीमा है कि गलत तरीके से पहले प्रदर्शित किया गया था की ओर लौटने, सब अच्छा है:

enter image description here

enter image description here

+0

आप पोस्ट कर सकते हैं आप कैसे कहते हैं 'TimelineAdapter (ArrayList timelineItems)' निर्माता:

mTimelineItems.set(position, item); 

इस तुलना में तेजी से होना चाहिए? – Andrew0x1

+0

मैंने पूरे टाइमलाइनफ्रैगमेंट को जोड़ा है - एडाप्टर को टुकड़े के ऑनक्रेट व्यू() में सेट किया जा रहा है। – kazume

उत्तर

2

अपने दूसरे दृष्टिकोण के बारे में। शायद आपका कोड काम नहीं कर रहा है क्योंकि आपके पास डेटा रेसmTimelineItems और mCachedEvents पर है। मैं आपका पूरा कोड नहीं देख सकता, लेकिन ऐसा लगता है कि आप बिना किसी सिंक्रनाइज़ेशन के यूआई थ्रेड के साथ doInBackground() के अंदर mTimelineItems का उपयोग कर रहे हैं।

मेरा प्रस्ताव है कि आप अपने पहले और दूसरे दृष्टिकोण का एक मिश्रण बनाने के लिए:

  1. मूल डेटा (mTimelineItems) की एक कॉपी बनाएं और इसे AsyncTask को भेजें।
  2. कॉपी को doInBackground() में असीमित रूप से बदलें और सभी परिवर्तन लॉग करें।
  3. बदले गए डेटा और UI थ्रेड पर लॉग लौटें।
  4. लॉग का उपयोग कर रीसाइक्लर व्यू में नया डेटा लागू करें।

मुझे कोड में इस दृष्टिकोण को स्पष्ट करने दें।

डेटा प्रबंधन:

public class AsyncDataUpdater 
{ 
    /** 
    * Example data entity. We will use it 
    * in our RecyclerView. 
    */ 
    public static class TimelineItem 
    { 
     public final String name; 
     public final float value; 

     public TimelineItem(String name, float value) 
     { 
      this.name = name; 
      this.value = value; 
     } 
    } 

    /** 
    * That's how we will apply our data changes 
    * on the RecyclerView. 
    */ 
    public static class Diff 
    { 
     // 0 - ADD; 1 - CHANGE; 2 - REMOVE; 
     final int command; 
     final int position; 

     Diff(int command, int position) 
     { 
      this.command = command; 
      this.position = position; 
     } 
    } 

    /** 
    * And that's how we will notify the RecyclerView 
    * about changes. 
    */ 
    public interface DataChangeListener 
    { 
     void onDataChanged(ArrayList<Diff> diffs); 
    } 


    private static class TaskResult 
    { 
     final ArrayList<Diff> diffs; 
     final ArrayList<TimelineItem> items; 

     TaskResult(ArrayList<TimelineItem> items, ArrayList<Diff> diffs) 
     { 
      this.diffs = diffs; 
      this.items = items; 
     } 
    } 

    private class InsertEventsTask extends AsyncTask<Void, Void, TaskResult> 
    { 
     //NOTE: this is copy of the original data. 
     private ArrayList<TimelineItem> _old_items; 

     InsertEventsTask(ArrayList<TimelineItem> items) 
     { 
      _old_items = items; 
     } 

     @Override 
     protected TaskResult doInBackground(Void... params) 
     { 
      ArrayList<Diff> diffs = new ArrayList<>(); 

      try 
      { 
       //TODO: long operation(Database, network, ...). 
       Thread.sleep(1000); 
      } 
      catch(InterruptedException e) 
      { 
       e.printStackTrace(); 
      } 

      //Some crazy manipulation with data... 
      //NOTE: we change the copy of the original data! 
      Random rand = new Random(); 
      for(int i = 0; i < 10; i ++) 
      { 
       float rnd = rand.nextFloat() * 100.0f; 
       for(int j = 0; j < _old_items.size(); j++) 
       { 
        if(_old_items.get(j).value > rnd) 
        { 
         TimelineItem item = new TimelineItem("Item " + rnd, rnd); 
         //Change data. 
         _old_items.add(j, item); 
         //Log the changes. 
         diffs.add(new Diff(0, j)); 
         break; 
        } 
       } 
      } 

      for(int i = 0; i < 5; i ++) 
      { 
       int rnd_index = rand.nextInt(_old_items.size()); 
       //Change data. 
       _old_items.remove(rnd_index); 
       //Log the changes. 
       diffs.add(new Diff(2, rnd_index)); 
      } 
      //... 

      return new TaskResult(_old_items, diffs); 
     } 

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

      //Apply the new data in the UI thread. 
      _items = result.items; 
      if(_listener != null) 
       _listener.onDataChanged(result.diffs); 
     } 
    } 

    private DataChangeListener _listener; 
    private InsertEventsTask _task = null; 

    /** Managed data. */ 
    private ArrayList<TimelineItem> _items = new ArrayList<>(); 

    public AsyncDataUpdater() 
    { 
     // Some test data. 
     for(float i = 10.0f; i <= 100.0f; i += 10.0f) 
      _items.add(new TimelineItem("Item " + i, i)); 
    } 

    public void setDataChangeListener(DataChangeListener listener) 
    { 
     _listener = listener; 
    } 

    public void updateDataAsync() 
    { 
     if(_task != null) 
      _task.cancel(true); 

     // NOTE: we should to make the new copy of the _items array. 
     _task = new InsertEventsTask(new ArrayList<>(_items)); 
     _task.execute(); 
    } 

    public int getItemsCount() 
    { 
     return _items.size(); 
    } 

    public TimelineItem getItem(int index) 
    { 
     return _items.get(index); 
    } 
} 

यूआई में उपयोग करते हुए:

public class MainActivity extends AppCompatActivity 
{ 
    private static class ViewHolder extends RecyclerView.ViewHolder 
    { 
     private final TextView name; 
     private final ProgressBar value; 

     ViewHolder(View itemView) 
     { 
      super(itemView); 

      name = (TextView)itemView.findViewById(R.id.tv_name); 
      value = (ProgressBar)itemView.findViewById(R.id.pb_value); 
     } 

     void bind(AsyncDataUpdater.TimelineItem item) 
     { 
      name.setText(item.name); 
      value.setProgress((int)item.value); 
     } 
    } 

    private static class Adapter extends RecyclerView.Adapter<ViewHolder> 
        implements AsyncDataUpdater.DataChangeListener 
    { 
     private final AsyncDataUpdater _data; 

     Adapter(AsyncDataUpdater data) 
     { 
      _data = data; 
      _data.setDataChangeListener(this); 
     } 

     @Override 
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
     { 
      View v = LayoutInflater.from(parent.getContext()) 
            .inflate(R.layout.list_item, parent, false); 
      return new ViewHolder(v); 
     } 

     @Override 
     public void onBindViewHolder(ViewHolder holder, int position) 
     { 
      holder.bind(_data.getItem(position)); 
     } 

     @Override 
     public int getItemCount() 
     { 
      return _data.getItemsCount(); 
     } 

     @Override 
     public void onDataChanged(ArrayList<AsyncDataUpdater.Diff> diffs) 
     { 
      //Apply changes. 
      for(AsyncDataUpdater.Diff d : diffs) 
      { 
       if(d.command == 0) 
        notifyItemInserted(d.position); 
       else if(d.command == 1) 
        notifyItemChanged(d.position); 
       else if(d.command == 2) 
        notifyItemRemoved(d.position); 
      } 
     } 
    } 

    private AsyncDataUpdater _data = new AsyncDataUpdater(); 

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

     RecyclerView rv_content = (RecyclerView)findViewById(R.id.rv_content); 
     rv_content.setLayoutManager(new LinearLayoutManager(this)); 
     rv_content.setAdapter(new Adapter(_data)); 

     Button btn_add = (Button)findViewById(R.id.btn_add); 
     btn_add.setOnClickListener(new View.OnClickListener() 
     { 
      @Override 
      public void onClick(View v) 
      { 
       _data.updateDataAsync(); 
      } 
     }); 
    } 
} 

मैं, जीएच पर Example आवेदन डाल ताकि आप इसे परीक्षण कर सकते हैं अगर आप चाहते हैं।

अद्यतन 1

बारे में डाटा रेस।

  1. this.mTimelineItems = timelineItems;TimelineAdapter() निर्माता अंदर ArrayList के संदर्भ की एक प्रति नहीं, बल्कि ArrayList ही की प्रतिलिपि बनाता है। तो आपके पास दो संदर्भ हैं: TimelineAdapter.mTimelineItems और Timeline.mTimelineItems, दोनों एक ही ArrayList ऑब्जेक्ट का संदर्भ लें। कृपया, this देखें।

  2. डेटा दौड़ जब doInBackground()कार्यकर्ता धागा और onProgressUpdate()यूआई धागा एक साथ से कहा जाता है से बुलाया होता है। मुख्य कारण यह है कि publishProgress()onProgressUpdate()सिंक्रनाइज़ पर कॉल नहीं करता है। इसके बजाय, publishProgress()भविष्य में यूआई थ्रेड पर के पर कॉल की योजना है। Here समस्या का एक अच्छा विवरण है।

विषय बंद करें।

यह:

mTimelineItems.remove(position); 
mTimelineItems.add(position, item); 
+0

धन्यवाद के लिए बहुत कुछ धन्यवाद, मेरे कोड में इसके साथ कुछ जादू करने की कोशिश कर रहा हूं। मुझे लगता है कि मैं वास्तव में डेटा की प्रतियों का उपयोग कर रहा हूं: टाइमलाइन इटम्स ऐरेलिस्ट जो मैं एडाप्टर के लिए डेटा सेट के रूप में उपयोग कर रहा हूं वह टाइमलाइन एटम्स की टाइमलाइन इटम्स ऐरेलिस्ट की एक प्रति है और डेटा बदलते समय, मैं विशिष्ट आइटम को "स्वैप" करता हूं एडाप्टर का डेटा सेट। मैं कुछ और कोड शामिल करने के लिए अपना ओपी अपडेट करूंगा। – kazume

+0

मुझे अभी एहसास हुआ, मुझे चीजों को थोड़ा सा उलटा करने की ज़रूरत है। आपके योगदान ने मुझे सही रास्ते पर रखा हालांकि - वहां बहुत सारी मूल्यवान चीजें हैं! मैं जल्द ही एक अपडेट के साथ वापस आऊंगा (आईएसएच) ... – kazume

+0

ओपी संपादित किया गया। ऐसा लगता है कि मेरी समस्या अधिक प्रदर्शन (एडाप्टर) समस्या है: -/ – kazume