版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010724819/article/details/75226377
之前周末的时候写过一篇复杂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
谢谢!