多类型Item-复杂RecyclerView的实现

之前周末的时候写过一篇复杂ListView相关的文章,但是写的不够详尽(具体到每个方法),只是个人觉得没有必要写的那么详细,因为大部分的内容对于一个拥有安卓开发开发经验的人来说跑完Demo之后都会很好理解的。

但是现如今使用ListView的部分朋友已经转到RecyclerView这一黑科技控件下了,所以我便将之前的思路试着看移植到RecyclerView之中,中间遇到了两个坑,下面来喝大家分享一下!

首先,按照规律,我们来看下效果图:

 

下面我们来一步步实现:

首先想到的是RecyclerView的一些设置,这些其实也没啥好说的:

看代码:

 

   myRecyclerViewAdapter = new MyRecyclerViewAdapter(this, users);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(myRecyclerViewAdapter);

主要是一些设置的东西。

 

 

下面我们来看重点:

 

MyRecyclerViewAdapter的实现

我们一步步进行分解

 

1.首先看顶部的ViewPager布局:


 

下面来看布局代码:

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/item1_vp"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:text="item00"/>
</LinearLayout>


其实也很简单直接声明下,之后我们来看ViewPager的Adapter设定,我这里就简单地设定了Adapter:

 

 

public class ViewPagerAdapter extends PagerAdapter {
    private Context context;

    public ViewPagerAdapter(Context context) {
        this.context = context;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        TextView textView = new TextView(context);
        textView.setText(position + "");
        container.addView(textView);
        return textView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public int getCount() {
        return 4;
    }
}

也是最最基础的东西了,没啥好说的,看不懂的童鞋面壁思过去。。。

 

2.两个线性布局,他俩存在的意义就是为了体现咱们的思路适应多种布局:

3.横向ScrollView,其实这里也可以用横向RecyclerView,只是这里为了避免嵌套引发的问题,使用了横向ScrollVIew:

布局如下:

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <HorizontalScrollView
        android:id="@+id/horizontalscrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <LinearLayout
            android:id="@+id/ll_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">
        </LinearLayout>
    </HorizontalScrollView>
</LinearLayout>


实现动态添加的代码如下:

 

 

 LinearLayout rl_layout = null;
            TextView tv_title = null;
            TextView tv_sub_title = null;
            ImageView iv_test = null;
            for (int i = 0; i < 10; i++) {
                View view = LayoutInflater.from(ctx).inflate(R.layout.scroll_item_layout, null);
                int screenWidth = CommonUtil.getScreenWidth(ctx);
                int screenHeight = CommonUtil.getScreenHeight(ctx);
                //根据屏幕宽度设定横向滑动子View的宽度
                view.setLayoutParams(new LinearLayout.LayoutParams(screenWidth / 2, 120));
                rl_layout = (LinearLayout) view.findViewById(R.id.rl_layout);
                tv_title = (TextView) view.findViewById(R.id.tv_title);
                tv_sub_title = (TextView) view.findViewById(R.id.tv_sub_title);
                tv_sub_title = (TextView) view.findViewById(R.id.tv_sub_title);
                iv_test = (ImageView) view.findViewById(R.id.iv_test);

                tv_title.setText("主标题" + i);
                tv_sub_title.setText("子标题" + i);
                iv_test.setImageResource(R.mipmap.ic_launcher);

                final int finalI = i;
                rl_layout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(ctx, "view" + finalI, Toast.LENGTH_SHORT).show();
                    }
                });

                itemHolder4.ll_main.addView(view);
            }

这里犹豫ScrollView只能有一个直接的子布局,所以我们在这里向LinearLayout中添加布局,然后再添加到HorizontalScrollView之中。

 

其次,为了保证布局中,横向滚动的ScrollView保证一屏之中只有两个,所以这里我们获取屏幕的宽度,然后设定每个子View的宽度为半个屏幕,哈哈!

下面就来到了最精彩的时候了:

我们应该怎样整合这些复杂的布局呢?

 

RecyclerView.Adapter<RecyclerView.ViewHolder>中给我们提供了几个非常好用的方法:
 
@Override public int getItemViewType(int position) {} 

   //为每种布局定义自己的ViewHolder
    public class ViewHolder1 extends RecyclerView.ViewHolder {
        private ViewPager item1_vp;

        public ViewHolder1(View itemView) {
            super(itemView);
            item1_vp = (ViewPager) itemView.findViewById(R.id.item1_vp);
        }

        public void setData() {
            item1_vp.setAdapter(new ViewPagerAdapter(ctx));
        }
    }

下面我们来一步步解释下:

创建ViewHolder的时候会调用该方法:
 
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { RecyclerView.ViewHolder holder = null; switch (viewType) { case TYPE1: holder = new ViewHolder1(View.inflate(ctx, R.layout.itemlayout1, null)); break; case TYPE2: holder = new ViewHolder2(View.inflate(ctx, R.layout.itemlayout2, null)); break; case TYPE3: holder = new ViewHolder3(View.inflate(ctx, R.layout.itemlayout3, null)); break; case TYPE4: holder = new ViewHolder4(View.inflate(ctx, R.layout.horizontal_crollview_main, null)); break; default: break; } return holder; } 

总共有四种类型,分别标记下:

 
//为三种布局定义一个标识 private final int TYPE1 = 0; private final int TYPE2 = 1; private final int TYPE3 = 2; private final int TYPE4 = 3;


绑定ViewHolder的时候,调用如下方法:

 
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { //由于木有相关的viewType参数,只能通过方法来获取了 int viewType = getItemViewType(position); switch (viewType) { case TYPE1: ((ViewHolder1) holder).setData(); break; case TYPE2: ((ViewHolder2) holder).setData(position); break; case TYPE3: ((ViewHolder3) holder).setData(position); break; case TYPE4: ((ViewHolder4) holder).setData((ViewHolder4) holder); break; } }


在这里我把设定的方法全部放到ViewHolder里面了。

这里我们需要根据返回类型的不同进行相关设定:
 
@Override public int getItemViewType(int position) { //获取当前布局的数据 User u = users.get(position); //哪个字段不为空就说明这是哪个布局 //比如第一个布局只有item1_str这个字段,那么就判断这个字段是不是为空, //如果不为空就表明这是第一个布局的数据 //根据字段是不是为空,判断当前应该加载的布局 Log.i("LHD", u.toString()); Log.i("LHD", "第一个返回值" + u.getItem1_str()); Log.i("LHD", "第二个返回值" + u.getItem2_str()); Log.i("LHD", "第三个返回值" + u.getItem3_str()); if (u.getItem1_str() != null) { return TYPE1; } else if (u.getItem2_str() != null) { return TYPE2; } else if (u.getItem3_str() != null) {//如果前两个字段都为空,那就一定是加载第三个布局啦。 return TYPE3; } else { return TYPE4; } }


这样就可以得出了各种条目类型以及对应关系。

但是看到这里是不是有点儿乱呢,乱就对了,说明你已经在认真思考了,哈哈!
我来总结下,处理复杂RecyclerView的方法:
1.重写
@Override public int getItemViewType(int position) {}

非常重要,这里是进行子条目布局区分的重要方法,

2.重写
 
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {}

方法可以根据type的不同返回不同的条目布局ViewHolder

3.重写:
 
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}

可以进行相关的绑定,也就是说白了进行初始化。
下面来贴上Adpater的完整源码:

 
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { //定义常用的参数 private Context ctx; private int resourceId; //JavaBean private List<User> users; private LayoutInflater inflater; //为三种布局定义一个标识 private final int TYPE1 = 0; private final int TYPE2 = 1; private final int TYPE3 = 2; private final int TYPE4 = 3; public MyRecyclerViewAdapter(Context ctx, List<User> objects) { this.ctx = ctx; this.users = objects; //别忘了初始化inflater inflater = LayoutInflater.from(ctx); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { RecyclerView.ViewHolder holder = null; switch (viewType) { case TYPE1: holder = new ViewHolder1(View.inflate(ctx, R.layout.itemlayout1, null)); break; case TYPE2: holder = new ViewHolder2(View.inflate(ctx, R.layout.itemlayout2, null)); break; case TYPE3: holder = new ViewHolder3(View.inflate(ctx, R.layout.itemlayout3, null)); break; case TYPE4: holder = new ViewHolder4(View.inflate(ctx, R.layout.horizontal_crollview_main, null)); break; default: break; } return holder; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { //由于木有相关的viewType参数,只能通过方法来获取了 int viewType = getItemViewType(position); switch (viewType) { case TYPE1: ((ViewHolder1) holder).setData(); break; case TYPE2: ((ViewHolder2) holder).setData(position); break; case TYPE3: ((ViewHolder3) holder).setData(position); break; case TYPE4: ((ViewHolder4) holder).setData((ViewHolder4) holder); break; } } @Override public int getItemViewType(int position) { //获取当前布局的数据 User u = users.get(position); //哪个字段不为空就说明这是哪个布局 //比如第一个布局只有item1_str这个字段,那么就判断这个字段是不是为空, //如果不为空就表明这是第一个布局的数据 //根据字段是不是为空,判断当前应该加载的布局 Log.i("LHD", u.toString()); Log.i("LHD", "第一个返回值" + u.getItem1_str()); Log.i("LHD", "第二个返回值" + u.getItem2_str()); Log.i("LHD", "第三个返回值" + u.getItem3_str()); if (u.getItem1_str() != null) { return TYPE1; } else if (u.getItem2_str() != null) { return TYPE2; } else if (u.getItem3_str() != null) {//如果前两个字段都为空,那就一定是加载第三个布局啦。 return TYPE3; } else { return TYPE4; } } @Override public int getItemCount() { return users.size(); } //为每种布局定义自己的ViewHolder public class ViewHolder1 extends RecyclerView.ViewHolder { private ViewPager item1_vp; public ViewHolder1(View itemView) { super(itemView); item1_vp = (ViewPager) itemView.findViewById(R.id.item1_vp); } public void setData() { item1_vp.setAdapter(new ViewPagerAdapter(ctx)); } } public class ViewHolder2 extends RecyclerView.ViewHolder { private TextView item2_tv; public ViewHolder2(View itemView) { super(itemView); item2_tv = (TextView) itemView.findViewById(R.id.item2_tv); } public void setData(int position) { item2_tv.setText(users.get(position).getItem2_str()); } } public class ViewHolder3 extends RecyclerView.ViewHolder { private Button item3_btn; public ViewHolder3(View itemView) { super(itemView); item3_btn = (Button) itemView.findViewById(R.id.item3_btn); } public void setData(int position) { item3_btn.setText(users.get(position).getItem3_str()); } } public class ViewHolder4 extends RecyclerView.ViewHolder { private LinearLayout ll_main; public ViewHolder4(View itemView) { super(itemView); ll_main = (LinearLayout) itemView.findViewById(R.id.ll_main); } public void setData(ViewHolder4 itemHolder4) { LinearLayout rl_layout = null; TextView tv_title = null; TextView tv_sub_title = null; ImageView iv_test = null; for (int i = 0; i < 10; i++) { View view = LayoutInflater.from(ctx).inflate(R.layout.scroll_item_layout, null); int screenWidth = CommonUtil.getScreenWidth(ctx); int screenHeight = CommonUtil.getScreenHeight(ctx); //根据屏幕宽度设定横向滑动子View的宽度 view.setLayoutParams(new LinearLayout.LayoutParams(screenWidth / 2, 120)); rl_layout = (LinearLayout) view.findViewById(R.id.rl_layout); tv_title = (TextView) view.findViewById(R.id.tv_title); tv_sub_title = (TextView) view.findViewById(R.id.tv_sub_title); tv_sub_title = (TextView) view.findViewById(R.id.tv_sub_title); iv_test = (ImageView) view.findViewById(R.id.iv_test); tv_title.setText("主标题" + i); tv_sub_title.setText("子标题" + i); iv_test.setImageResource(R.mipmap.ic_launcher); final int finalI = i; rl_layout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(ctx, "view" + finalI, Toast.LENGTH_SHORT).show(); } }); itemHolder4.ll_main.addView(view); } } } }

 


我在编写代码中遇到的两个坑:

 

子布局已经设置match_parent了,但是条目布局还是包裹内容的形式展示出来了,这里做了个处理就好了,
 
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/colorBlack" />

在布局的最下面加了一个View布局,撑开了整个布局条目,使之匹配父布局,目前原因未知,先这样处理了。

2.刷新控件适应的是XRefreshView,非常感谢作者啊,但是在上拉加载的时候始终会遮盖最新的条目,使之没法展示出来,这里我做了个小小的处理:
 
recyclerView.scrollBy(0,30);

上滑一段距离,这样就可以展示出来了,提示用户下面还有内容可以下拉查看。嗯嗯,还行吧!

下面照旧是放出源码的时候了:

ComplexRecyclerView
谢谢!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值