示例图片:
1.gif
实现思路:
开发文档要求这个页面,上半部分不动,下半部分当一个页面展示不全,是可以滑动的。
我最初的想法是 ScrollView + RecyclerView 。但是在机器上运行之后,发现控件之间发生了冲突,RecyclerView 显示不全。经过了一段时间的思考,最终选择使用 RecyclerView + RecyclerView 实现页面滑动和多个不同类别的 item 展示。
其实网上关于 RecyclerView 的相关文档有很多,但是我觉得都是打补丁的方法,没有真正意义上的使用 RecyclerView 。
代码实现(核心代码部分):
从示例图中,可以看到,整个页面分为两大部分 A 是不可以滑动的上半部分,B 是可以滑动的下半部分。而 B 又分为三个小部分,B1 是一大堆文字描述,B2 是一个用于展示有多少个演员的 RecyclerView ,B3 是一个用于展示有多少个作品的 RecyclerView 。
那么很显然,我们要在主布局页面创建一个 RecyclerView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
...
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
并在主类里面将 B1,B2,B3,按照顺序一次添加:
public class MainActivity extends AppCompatActivity {
private List<Integer> mTypeList = new ArrayList<>();
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initParam();
...
}
private void initParam() {
mTypeList.add(1); // B1
mTypeList.add(2); // B2
mTypeList.add(3); // B3
}
...
}
然后就是我们的核心部分 Adapter ,继承 RecyclerView.Adapter<RecyclerView.ViewHolder>
定义 B1,B2,B3 三个常量:
private static final int TYPE_B1 = 1;
private static final int TYPE_B2 = 2;
private static final int TYPE_B3 = 3;
重写 getItemViewType 方法,参数 position 代表 RecyclerView 的位置,而 int 型的返回值代表了布局的类型,即 B1 是 1,B2 是 2,B3 是 3。
@Override
public int getItemViewType(int position) {
if (mTypeList.get(position) == 1) { // B1
return TYPE_B1;
} else if (mTypeList.get(position) == 2) { // B2
return TYPE_B2;
} else if (mTypeList.get(position) == 3) { // B3
return TYPE_B3;
} else {
return 0;
}
}
自定义三个 ViewHolder:
public class B1ViewHolder extends RecyclerView.ViewHolder {
public MainDetailsViewHolder(View itemView) {
super(itemView);
}
}
public class B2ViewHolder extends RecyclerView.ViewHolder {
public RecyclerView b2RecyclerView;
public MainActorsViewHolder(View itemView) {
super(itemView);
aRecyclerView = itemView.findViewById(R.id.rcv_b2);
}
}
public class B3ViewHolder extends RecyclerView.ViewHolder {
public RecyclerView b3RecyclerView;
public MainActorsViewHolder(View itemView) {
super(itemView);
aRecyclerView = itemView.findViewById(R.id.rcv_b3);
}
}
重写 onCreateViewHolder 方法,返回一个自定义的 ViewHolder (当 RecyclerView 需要一个 ViewHolder 时会回调该方法,如果有可复用的 View 不会回调)。
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_B1) { // B1
View viewB1 = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_b1, parent, false);
B1ViewHolder b1Holder = new B1ViewHolder(viewB1);
return b1Holder;
} else if (viewType == TYPE_B2) { // B2
View viewB2 = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_b2, parent, false);
B2ViewHolder b2Holder = new B2ViewHolder(viewB2);
return b2Holder;
} else if (viewType == TYPE_B3) { // B3
View viewB3 = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_b3, parent, false);
B3ViewHolder b3Holder = new B3ViewHolder(viewB3);
return b3Holder;
}
return null;
}
重写 onBindViewHolder 方法,填充 onCreateViewHolder 方法返回的 holder 中的控(当一个 View 需要出现在屏幕上时,该方法会被回调,我们需要再该方法中根据数据来更改视图)。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof B2ViewHolder) {
setB2Data((B2ViewHolder) holder);
} else if (holder instanceof B3ViewHolder) {
setB3Data((B3ViewHolder) holder);
}
}
重写 getItemCount 方法,获取数据的数量(告诉 RecyclerView 有多少个视图需要显示)。
private final Context mContext;
private final List<Integer> mTypeList;
public MainAdapter(Context context, List<Integer> list) {
this.mContext = context;
this.mTypeList = list;
}
@Override
public int getItemCount() {
return mTypeList.size();
}
上面代码涉及到三个子布局 layout_b1 、layout_b2 、layout_b3 。
layout_b1 从上面的示例图中可以看到就是一堆文字排列。
layout_b2 、layout_b3 是我们今天的重点:RecyclerView 嵌套 RecyclerView 。
<?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="wrap_content"
android:orientation="vertical">
...
<android.support.v7.widget.RecyclerView
android:id="@+id/rcv_b2"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
既然有 RecyclerView 那么就会有 Adapter ,自定义 B2 和 B3 对应的 Adapter 。
(item 布局就不啰嗦了,很简单)
public class B2Adapter extends RecyclerView.Adapter<B2Adapter.B2Holder> {
private final Context mContext;
private OnItemClickListener mListener;
public B2Adapter(Context context) {
this.mContext = context;
}
@Override
public B2Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext)
.inflate(R.layout.item_b2, parent, false);
B2Holder holder = new B2Holder(view);
return holder;
}
@Override
public void onBindViewHolder(final B2Holder holder, int position) {
if (mListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.onItemClick(holder.getLayoutPosition());
}
});
}
}
@Override
public int getItemCount() {
return 5;
}
public class B2Holder extends RecyclerView.ViewHolder {
public B2Holder(View itemView) {
super(itemView);
}
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mListener = onItemClickListener;
}
}
public interface OnItemClickListener {
void onItemClick(int position);
}
B3 和 B2 类似,就不赘述了。
自定义完 子 Adapter ,我们就要回到主 Adapter 里面去调用。
private void setB2Data(B2ViewHolder holder) {
setB2RecyclerView(holder.b2RecyclerView);
}
private void setB2RecyclerView(RecyclerView b2RecyclerView) {
B2Adapter b2Adapter = new B2Adapter(mContext);
LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
layoutManager.setOrientation(OrientationHelper.VERTICAL);
b2RecyclerView.setLayoutManager(layoutManager);
b2RecyclerView.setHasFixedSize(false);
b2RecyclerView.setAdapter(b2Adapter);
b2Adapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(int position) {
// TODO:
}
});
}
B3 和 B2 类似,就不赘述了。
注意:
如果我们的界面是嵌套在可以左右滑动的控件里,这时会发生焦点的问题,导致整体界面向上移动一小部分,解决办法是在布局的最外层加入:
android:focusable="true"
android:focusableInTouchMode="true"
综上所述,就完成了 RecyclerView 的使用,避免了 ScrollView + RecyclerView 在一起使用会出现冲突的问题。
网友评论