Android RecyclerView的ViewHolder和Ada

fondtiger發表於2021-09-09

前一段時間,因為專案需要使用了RecyclerView,為了方便使用還進行封裝了,詳細參見此處:

那樣的封裝有幾個問題:

1.   ViewHolder的setData(M data)雖然方便設定資料,但是ViewHolder需要知曉資料型別。ViewHolder應該只用作View的快取,而不應該知曉填充View的資料。

2.   BaseAdapter無法新增Header和Footer。

3.   點選事件耦合性較高。

基於以上幾點,對BaseHolder和BaseAdapter進行修改最佳化。

BaseHolder的最佳化

相對於舊版的BaseHolder:

1.   新版的去除的資料型別的注入,使ViewHolder只用來快取View。

2.   新增SparseArray,使之來快取View。

3.   新增BaseHolder(View view)構造器,外部更方便控制View。

4.   保留getContext()方法,方便獲取Context物件。

新版的BaseHolder程式碼如下:

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

public class BaseHolder extends RecyclerView.ViewHolder   {

  

    private SparseArray viewArray;

  

    

    public BaseHolder(ViewGroup   parent, @LayoutRes int resId) {

        super(LayoutInflater.from(parent.getContext()).inflate(resId,   parent, false));

        viewArray   = new SparseArray();

    }

  

    

    public BaseHolder(View view) {

        super(view);

        viewArray   = new SparseArray();

    }

  

    

    protected T getView(@IdRes int viewId){

        View   view = viewArray.get(viewId);

        if (view   == null) {

            view   = itemView.findViewById(viewId);

            viewArray.put(viewId,   view);

        }

        return (T)   view;

    }

  

    

    protected Context getContext()   {

        return itemView.getContext();

    }

}

 

Adapter部分的最佳化

Adapter拆分為兩個抽象類:AbsAdapter與BaseAdapter,其中:
AbsAdapter:封裝了和ViewHolder和HeaderView,FooterView相關的方法。
BaseAdapter:繼承AbsAdapter,封裝了資料相關的方法。

各自聚焦於不同的方面,方面日後擴充套件。

AbsAdapter的程式碼如下:

[程式碼]java程式碼:

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022

023

024

025

026

027

028

029

030

031

032

033

034

035

036

037

038

039

040

041

042

043

044

045

046

047

048

049

050

051

052

053

054

055

056

057

058

059

060

061

062

063

064

065

066

067

068

069

070

071

072

073

074

075

076

077

078

079

080

081

082

083

084

085

086

087

088

089

090

091

092

093

094

095

096

097

098

099

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

  

public abstract class AbsAdapter extends RecyclerView.Adapter {

  

    private static final String TAG   = "AbsAdapter";

  

    public static final int VIEW_TYPE_HEADER   = 1024;

    public static final int VIEW_TYPE_FOOTER   = 1025;

  

    protected View headerView;

    protected View footerView;

  

    protected Context context;

  

    public AbsAdapter(Context   context) {

        this.context   = context;

    }

  

    @Override

    public final BaseHolder   onCreateViewHolder(ViewGroup parent, int viewType) {

        if (viewType   == VIEW_TYPE_HEADER) {

            return   new BaseHolder(headerView);

        } else if   (viewType == VIEW_TYPE_FOOTER) {

            return   new BaseHolder(footerView);

        } else {

            return   createCustomViewHolder(parent, viewType);

        }

    }

  

    

    public abstract VH   createCustomViewHolder(ViewGroup parent, int viewType);

  

    @Override

    public final void onBindViewHolder(BaseHolder   holder, int position) {

        switch (holder.getItemViewType())   {

            case   VIEW_TYPE_HEADER:

            case   VIEW_TYPE_FOOTER:

                break;

            default:

                bindCustomViewHolder((VH)   holder, position);

                break;

        }

    }

  

    

    public abstract void bindCustomViewHolder(VH   holder, int position);

  

    

    public void addHeaderView(View   headerView) {

        if (headerView   == null) {

            Log.w(TAG,   "add the header view is null");

            return   ;

        }

        this.headerView   = headerView;

        notifyDataSetChanged();

    }

  

    

    public void removeHeaderView()   {

        if (headerView   != null) {

            headerView   = null;

            notifyDataSetChanged();

        }

    }

  

    

    public void addFooterView(View   footerView) {

        if (footerView   == null) {

            Log.w(TAG,   "add the footer view is null");

            return;

        }

        this.footerView   = footerView;

        notifyDataSetChanged();

    }

  

    

    public void removeFooterView()   {

        if (footerView   != null) {

            footerView   = null;

            notifyDataSetChanged();

        }

    }

  

    

    public int getExtraViewCount()   {

        int extraViewCount   = 0;

        if (headerView   != null) {

            extraViewCount++;

        }

        if (footerView   != null) {

            extraViewCount++;

        }

        return extraViewCount;

    }

  

    

    public int getHeaderExtraViewCount()   {

        return headerView   == null ? 0 : 1;

    }

  

    

    public int getFooterExtraViewCount()   {

        return footerView   == null ? 0 : 1;

    }

  

    @Override

    public abstract long getItemId(int   position);

},>

 

BaseAdapter的程式碼如下:

[程式碼]java程式碼:

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022

023

024

025

026

027

028

029

030

031

032

033

034

035

036

037

038

039

040

041

042

043

044

045

046

047

048

049

050

051

052

053

054

055

056

057

058

059

060

061

062

063

064

065

066

067

068

069

070

071

072

073

074

075

076

077

078

079

080

081

082

083

084

085

086

087

088

089

090

091

092

093

094

095

096

097

098

099

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

  

public abstract class BaseAdapter extends AbsAdapter {

  

    private List dataList;

  

    public BaseAdapter(Context   context) {

        super(context);

        this.dataList   = new ArrayList();

    }

  

    public BaseAdapter(Context   context, List list) {

        super(context);

        this.dataList   = new ArrayList();

        this.dataList.addAll(list);

    }

  

    

    public boolean fillList(List   list) {

        dataList.clear();

        boolean   result = dataList.addAll(list);

        if (result)   {

            notifyDataSetChanged();

        }

        return result;

    }

  

    

    public boolean appendItem(M   data) {

        boolean   result = dataList.add(data);

        if (result)   {

            if   (getHeaderExtraViewCount() == 0) {

                notifyItemInserted(dataList.size()   - 1);

            }   else {

                notifyItemInserted(dataList.size());

            }

        }

        return result;

    }

  

    

    public boolean appendList(List   list) {

        boolean   result = dataList.addAll(list);

        if (result)   {

            notifyDataSetChanged();

        }

        return result;

    }

  

    

    public void proposeItem(M data)   {

        dataList.add(0,   data);

        if (getHeaderExtraViewCount()   == 0) {

            notifyItemInserted(0);

        } else {

            notifyItemInserted(getHeaderExtraViewCount());

        }

    }

  

    

    public void proposeList(List   list) {

        dataList.addAll(0,   list);

        notifyDataSetChanged();

    }

  

    @Override

    public long getItemId(int position)   {

        return position;

    }

  

    @Override

    public final int getItemViewType(int   position) {

        if (headerView   != null && position == 0) {

            return   VIEW_TYPE_HEADER;

        } else if   (footerView != null && position == dataList.size() +   getHeaderExtraViewCount()) {

            return   VIEW_TYPE_FOOTER;

        } else {

            return   getCustomViewType(position);

        }

    }

  

    

    public abstract int getCustomViewType(int   position);

  

    @Override

    public int getItemCount() {

        return dataList.size()   + getExtraViewCount();

    }

  

    

    public M getItem(int position)   {

        if (headerView   != null && position == 0

                ||   position >= dataList.size() + getHeaderExtraViewCount()) {

            return   null;

        }

        return headerView   == null ? dataList.get(position) : dataList.get(position - 1);

    }

  

    

    public M getItem(VH holder) {

        return getItem(holder.getAdapterPosition());

    }

  

    public void updateItem(M data)   {

        int index   = dataList.indexOf(data);

        if (index  

            return;

        }

        dataList.set(index,   data);

        if (headerView   == null) {

            notifyItemChanged(index);

        } else {

            notifyItemChanged(index   + 1);

        }

    }

  

    

    public void removeItem(int position)   {

        if (headerView   == null) {

            dataList.remove(position);

        } else {

            dataList.remove(position   - 1);

        }

        notifyItemRemoved(position);

    }

  

    

    public void removeItem(M data)   {

        int index   = dataList.indexOf(data);

        if (index  

            return;

        }

        dataList.remove(index);

        if (headerView   == null) {

            notifyItemRemoved(index);

        } else {

            notifyItemRemoved(index   + 1);

        }

    }

},>,>

 

點選事件的最佳化

為了降低點選事件的耦合性,將點選事件從Adapter中移除,使用另外一種方式來實現。使用程式碼如下:

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(recyclerView,

        new RecyclerItemClickListener.OnItemClickListener()   {

            @Override

            public   void onItemClick(View view, int position) {

                Person   person = singleAdapter.getItem(position);

                Toast.makeText(SingleActivity.this,   "click:" + person,

                        Toast.LENGTH_SHORT).show();

            }

  

            @Override

            public   void onItemLongClick(View view, int position) {

                Person   person = singleAdapter.getItem(position);

                Toast.makeText(SingleActivity.this,   "Long Click:" + person,

                        Toast.LENGTH_SHORT).show();

            }

        }));

 

使用RecyclerView的自帶的方法addOnItemTouchListener()實現點選和長點選事件。實現程式碼如下:

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

  

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener   {

  

    private OnItemClickListener   clickListener;

    private GestureDetector   gestureDetector;

  

    public interface OnItemClickListener   {

        

        void onItemClick(View   view, int position);

  

        

        void onItemLongClick(View   view, int position);

    }

  

    public RecyclerItemClickListener(final   RecyclerView recyclerView,

                                     OnItemClickListener   listener) {

        this.clickListener   = listener;

        gestureDetector   = new GestureDetector(recyclerView.getContext(),

                new   GestureDetector.SimpleOnGestureListener() {

                    @Override

                    public   boolean onSingleTapUp(MotionEvent e) {

                        return   true;

                    }

  

                    @Override

                    public   void onLongPress(MotionEvent e) {

                        View   childView = recyclerView.findChildViewUnder(e.getX(), e.getY());

                        if   (childView != null && clickListener != null) {

                            clickListener.onItemLongClick(childView,

                                    recyclerView.getChildAdapterPosition(childView));

                        }

                    }

                });

    }

  

    @Override

    public boolean onInterceptTouchEvent(RecyclerView   rv, MotionEvent e) {

        View   childView = rv.findChildViewUnder(e.getX(), e.getY());

        if (childView   != null && clickListener != null &&   gestureDetector.onTouchEvent(e)) {

            clickListener.onItemClick(childView,   rv.getChildAdapterPosition(childView));

            return   true;

        }

        return false;

    }

  

    @Override

    public void onTouchEvent(RecyclerView   rv, MotionEvent e) {

  

    }

  

    @Override

    public void onRequestDisallowInterceptTouchEvent(boolean   disallowIntercept) {

  

    }

}

 

分割線的最佳化

RecyclerView的分割線不像ListView那樣方便,但是RecyclerView也提供了``方法來新增分割線。

分割線程式碼使用的是SDK中的Sample的程式碼,在其中新增了``方法,方便設定分割線的形式。

具體的程式碼參見此處:

最佳化後的使用

最佳化後使用方式有所改變,具體使用方式如下:

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

recyclerView = (RecyclerView)   findViewById(R.id.single_rv);

layoutManager = new LinearLayoutManager(this);

recyclerView.setLayoutManager(layoutManager);

DividerDecoration decoration = new DividerDecoration(this,   DividerDecoration.VERTICAL_LIST);

Drawable drawable =   getResources().getDrawable(R.drawable.divider_single);

decoration.setDivider(drawable);

recyclerView.addItemDecoration(decoration);

recyclerView.setAdapter(singleAdapter);

  

View view =   LayoutInflater.from(this).inflate(R.layout.item_single_header, null, false);

singleAdapter.addHeaderView(view);

  

recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(recyclerView,

        new RecyclerItemClickListener.OnItemClickListener()   {

            @Override

            public   void onItemClick(View view, int position) {

                Person   person = singleAdapter.getItem(position);

                Toast.makeText(SingleActivity.this,   "click:" + person,

                        Toast.LENGTH_SHORT).show();

            }

  

            @Override

            public   void onItemLongClick(View view, int position) {

                Person   person = singleAdapter.getItem(position);

                Toast.makeText(SingleActivity.this,   "Long Click:" + person,

                        Toast.LENGTH_SHORT).show();

            }

        }));

 

以上所有的程式碼均在GitHub上,具體地址在此:

原文連結:http://www.apkbus.com/blog-705730-61351.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2508/viewspace-2814823/,如需轉載,請註明出處,否則將追究法律責任。

相關文章