首先啥也别说,先引入依赖包。
compile 'com.android.support:cardview-v7:22.2.1'
compile 'com.android.support:recyclerview-v7:22.2.1'
CardView
CardView继承自FrameLayout,其中有一个比较常用的属性,就是指定其Cornerapp:cardCornerRadius="5dp"
,一个CardView的简单示例如下。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="4dp"
android:clickable="true"
app:cardCornerRadius="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:clickable="true"
android:text="@string/hello_world"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
我们完全可以把CardView换成FrameLayout,然后给他一个带有corner的shape,出来的效果也没差。
res/drawable/shape
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp"/>
<stroke
android:width="1px"
android:color="#babdff"/>
</shape>
另外CardView不能响应selector,而FrameLayout则可以。
res/drawable/selector
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/clr_normal" android:state_pressed="false"/>
<item android:drawable="@drawable/clr_pressed" android:state_pressed="true"/>
</selector>
综上所述,并没有在实际使用中感觉有什么特别好用的地方,难道有什么其他更屌的属性?
RecyclerView
RecyclerView被设计出来取代ListView,因为其强大的灵活性。
RecyclerView的基本使用和ListView比较相似
1、xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
2、core code
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); // 布局管理器
List<String> list = new ArrayList<>();
list.add("Java");
list.add("C");
list.add("C++");
mRecyclerView.setAdapter(new MyAdapter(list)); // 一样需要设置Adapter
3、adapter,我们直接拿上面的CardView作为加载的View。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mList;
public MyAdapter(List<String> list) {
mList = list;
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);
ViewHolder viewHolder = new ViewHolder(itemView);
return viewHolder;
}
@Override
public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
holder.mTextView.setText(mList.get(position));
}
@Override
public int getItemCount() {
return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
CardView mCardView;
TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mCardView = (CardView) itemView.findViewById(R.id.cardView);
mTextView = (TextView) itemView.findViewById(R.id.textView);
}
}
}
4、RecyclerView比较坑爹的地方是不能直接添加点击事件,需要自己实现,不过实现起来也并不难。在Adapter中操作,增加一个回调接口,然后在onBindViewHolder方法中设置回调。
OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
@Override
public void onBindViewHolder(final MyAdapter.ViewHolder holder, int position) {
holder.mTextView.setText(mList.get(position));
if (mOnItemClickListener != null) {
holder.mCardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getLayoutPosition();
mOnItemClickListener.onItemClick(v, pos);
}
});
}
}
点击后有旋转动画
5、数据更新高大上动画,我们通过adapter.notifyDataSetChanged()
去更新数据并不会有动画效果。但是一旦我们用adapter.notifyItemInserted()
oradapter.notifyItemRemoved()
就会有很炫的动画效果。为了方便操作,直接写进Adapter中。
public void addData(int position, String data) {
mList.add(position, data);
notifyItemInserted(position);
}
public void removeData(int position) {
mList.remove(position);
notifyItemRemoved(position);
}
我们一开始就讲了RecyclerView有强大的灵活性,那么究竟灵活在哪呢?举个例子,如果我们将布局管理器改为如下,分分钟就变成GridView了。
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, 2);
mRecyclerView.setLayoutManager(layoutManager); // 布局管理器
这种灵活性也体现在ItemDecoration、ItemAnimator,包括LayoutManager,这些都是抽象基类,也就是我们完全可以自己定制出想要的divider、更新数据的动画效果还有布局方式。
最后其实我们不应只将RecyclerView视作ListView的升级版,而应该是当展示大量数据时都应考虑的灵活View。
A flexible view for providing a limited window into a large data set.
SwipeRefreshLayout
为什么会把SwipeRefreshLayout也放在这里,纯属是因为以前经常会有下拉刷新然后更新数据的操作,所以就想和RecyclerView一起用咯。
This layout should be made the parent of the view that will be refreshed as a result of the gesture and can only support one direct child.
根据上文,SwipeRefreshLayout只能有一个childView,我们直接将其包装在上面的RecyclerView上。
1、xml
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
...
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
2、使用
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
adapter.addData(0, "C#");
mSwipeRefreshLayout.setRefreshing(false); // 隐藏刷新条
}
}, 2000);
}
});
上面就是最基本的使用了,注册一个监听器,然后完成任务后将刷新圈圈隐藏。至于其他的设置可以查看API。
我想要一个进入界面后自动刷新
的效果,onCreate
方法中直接调用
mSwipeRefreshLayout.setRefreshing(true);
结果是完全不work,因为窗口还没初始化完。将其改写到onWindowFocusChanged
方法则可以,但是仅仅弹出刷新的动画,而不会执行listener的监听操作。
If an activity wishes to show just the progress animation, it should call setRefreshing(true)
最后,比较合适的方案是,在onCreate中调用post方法,post本质是通过Handler进行异步处理,当界面初始化完后弹出刷新的小圆圈并且手动调用listener.onRefresh()操作。这样就完美实现了进入Activity后自动刷新的效果了。
final SwipeRefreshLayout.OnRefreshListener listener = new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
adapter.addData(0, "C#");
mSwipeRefreshLayout.setRefreshing(false);
}
}, 2000);
}
};
mSwipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
listener.onRefresh();
}
});
mSwipeRefreshLayout.setOnRefreshListener(listener);