在谷歌给我们提供的appcompat包中有很多为兼容而生的控件,这样就可以做到高低版本和不同的ROM之间体验一致!还可以配合appcompat的主题使用达到体验一致性。
appcompat控件以及样式
- 文本 AppCompatTextView
- 按钮 AppCompatButton
- 提示框 android.support.v7.app.AlertDialog`
- 文本输入框 AppCompatEditText
- 下拉刷新 SwipeRefreshLayout
- 进度条样式 style=”@style/Widget.AppCompat.ProgressBar.Horizontal”
- 列表Popup框 ListPopupWindow
- 菜单Popup框 PopupMenu
- 列表间隔线 LinearLayoutCompat
- RecyclerView android.support.v7.widget.RecyclerView
接下来让我们来看一下这些控件的一些基本使用方法。
AppCompatTextView
<android.support.v7.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:text="Hello World" />
AppCompatButton
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:onClick="showDialog"
android:text="对话框" />
ProgressBarStyle
<ProgressBar
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="16dp" />
AppCompatEditText
<android.support.v7.widget.AppCompatEditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:text="呵呵" />
显示效果如图:
可以看到整个界面都是MD的一个风格
android.support.v7.app.AlertDialog
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("XY的提示框");
builder.setMessage("给XY一个女朋友");
builder.setPositiveButton("好的", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton("不行", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
ListPopupwindow
public void showListPopupwindow(View view){
String[] datas = {"条目0","条目1","条目2","条目3","条目4"};
ListAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,datas);
ListPopupWindow listPopupWindow = new ListPopupWindow(this);
listPopupWindow.setAdapter(adapter);
listPopupWindow.setAnchorView(view);
listPopupWindow.show();
}
PopupMenu
public void showPopupmenuwindow(View view){
PopupMenu popupMenu = new PopupMenu(this,view);
popupMenu.getMenuInflater().inflate(R.menu.main,popupMenu.getMenu());
popupMenu.show();
}
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:icon="@android:drawable/ic_menu_set_as"
android:title="设置"/>
<item
android:id="@+id/action_share"
android:orderInCategory="101"
android:icon="@android:drawable/ic_menu_share"
android:title="分享"/>
<item
android:id="@+id/action_new"
android:orderInCategory="102"
android:icon="@android:drawable/ic_menu_add"
android:title="添加"/>
</menu>
LinearLayoutCompat
<android.support.v7.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:divider="@drawable/abc_list_divider_mtrl_alpha"
app:showDividers="beginning|middle">
<android.support.v7.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:text="Hello World" />
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:onClick="showDialog"
android:text="对话框" />
<ProgressBar
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="16dp" />
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:onClick="showListPopupwindow"
android:text="显示ListPopupwindow" />
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:onClick="showPopupmenuwindow"
android:text="显示showPopupmenuwindow" />
<android.support.v7.widget.AppCompatEditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="5dp"
android:text="呵呵" />
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:onClick="toRecyclerView"
android:layout_marginBottom="5dp"
android:text="RecyclerView" />
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:onClick="toHeaderRecyclerView"
android:layout_marginBottom="5dp"
android:text="带headerView以及footerView的RecyclerView" />
</android.support.v7.widget.LinearLayoutCompat>
可以看到每一个childView之间都有一根间隔线
app:showDividers=”beginning|middle”的意思就是第一个子view上有间隔线以及每一个子view之间也有间隔线
下面要说到最重要的一个控件RecyclerView了,可以说是ListView和GridView的增强升级版,但是RecyclerView比这两个哥们要牛逼的多,话不多说,直接上代码
public class RecyclerViewActivity extends AppCompatActivity{
private Handler handler = new Handler();
private RecyclerView rv;
private List<String> items;
private XYAdapter xyAdapter;
private RecyclerView.ItemDecoration dividerItemDecoration;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
items = new ArrayList<>();
for (int i = 0; i < 90; i++) {
items.add("条目" + i);
}
rv = (RecyclerView) findViewById(R.id.recyclerview);
xyAdapter = new XYAdapter(items);
xyAdapter.setOnItemClickListener(new XYBaseAdapter.OnItemClickListener() {
@Override
public void OnItemClick(View v, int position) {
Toast.makeText(RecyclerViewActivity.this, "点击" + position, Toast.LENGTH_SHORT).show();
}
});
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
rv.setAdapter(xyAdapter);
rv.setItemAnimator(new DefaultItemAnimator());
/**
* 添加条目间隔线
*/
dividerItemDecoration = new DividerItemDecoration(this,LinearLayoutManager.VERTICAL);
rv.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new XYItemTouchHelperCallBack(xyAdapter));
itemTouchHelper.attachToRecyclerView(rv);
}
/**
* 添加一个item
* @param view
*/
public void addItem(View view){
xyAdapter.addData("add item",3);
}
/**
* 删除一个item
* @param view
*/
public void removeItem(View view){
xyAdapter.removeData(3);
}
}
重点在这两行代码:
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
rv.setAdapter(xyAdapter);
recyclerView设置LayoutManager来管理布局,设置Adapter来绑定数据,来看一下Adapter如何实现
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class XYBaseAdapter<T> extends RecyclerView.Adapter<XYBaseAdapter<T>.XYViewHolder> {
protected List<T> mItems;
private OnItemClickListener mOnItemClickListener;
public XYBaseAdapter(List<T> items) {
mItems = items;
}
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
@Override
public XYViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View root = LayoutInflater.from(parent.getContext()).inflate(getContentLayoutId(), parent, false);
XYViewHolder viewHolder = new XYViewHolder(root);
return viewHolder;
}
@Override
public void onBindViewHolder(XYViewHolder holder, int position) {
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
lp.height = 100;
holder.itemView.setOnClickListener(new XYOnItemClickListener(holder.getLayoutPosition()));
bindData(holder, position);
}
protected abstract int getContentLayoutId();
protected abstract void bindData(XYViewHolder holder, int position);
@Override
public int getItemCount() {
return mItems.size();
}
/**
* 刷新数据
* @param datas
*/
public void refresh(List<T> datas){
this.mItems.clear();
this.mItems.addAll(datas);
notifyDataSetChanged();
}
/**
* 添加数据
* @param datas
*/
public void addData(List<T> datas){
this.mItems.addAll(datas);
notifyDataSetChanged();
}
public void addData(T t,int position){
this.mItems.add(position,t);
notifyItemInserted(position);
}
public void removeData(int position){
this.mItems.remove(position);
notifyItemRemoved(position);
}
public void removeData(){
this.mItems.clear();
notifyDataSetChanged();
}
class XYViewHolder extends RecyclerView.ViewHolder {
private Map<Integer, View> mCache = new HashMap<>();
public XYViewHolder(View itemView) {
super(itemView);
}
public View findView(int res) {
if (mCache.get(res) == null) {
mCache.put(res, itemView.findViewById(res));
}
return mCache.get(res);
}
}
public interface OnItemClickListener {
void OnItemClick(View v, int position);
}
public class XYOnItemClickListener implements View.OnClickListener {
int mPosition;
public XYOnItemClickListener(int position) {
mPosition = position;
}
@Override
public void onClick(View v) {
if (mOnItemClickListener != null)
mOnItemClickListener.OnItemClick(v, mPosition);
}
}
}
方法onCreateViewHolder中创建返回ViewHolder,在onBindViewHolder方法中绑定相关数据,此处抽象优化出来让子类继承实现bindData方法,getContentLayoutId方法实现类返回item的局部id,看一下XYBaseAdapter的实现类
public class XYAdapter extends XYBaseAdapter<String> implements XYMoveListener {
public XYAdapter(List<String> items) {
super(items);
}
@Override
protected int getContentLayoutId() {
return R.layout.recyclerview_item;
}
@Override
protected void bindData(XYViewHolder holder, int position) {
TextView tv = (TextView) holder.findView(R.id.content);
tv.setText(mItems.get(position) + "");
}
}
看一下效果
图中每一个item之间都有一条间隔线,这个要怎么做到呢?我们需要用到ItemDecoration类了,代码撸起来
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private int[] attrs = new int[]{android.R.attr.listDivider};
private Drawable dividerDrawable;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
TypedArray ta = context.obtainStyledAttributes(attrs);
dividerDrawable = ta.getDrawable(0);
ta.recycle();
setOrientation(orientation);
}
private void setOrientation(int orientation) {
if (orientation != LinearLayoutManager.HORIZONTAL && orientation != LinearLayoutManager.VERTICAL) {
throw new IllegalArgumentException("布局参数错误");
}
this.mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
drawVerticalDivider(c, parent);
} else if (mOrientation == LinearLayoutManager.VERTICAL) {
drawHorizontalDivider(c, parent);
}
}
private void drawVerticalDivider(Canvas c, RecyclerView parent) {
int top = parent.getPaddingTop();
for (int i = 0; i < parent.getChildCount(); i++) {
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
int bottom = childView.getBottom() + params.bottomMargin;
int left = childView.getRight() + params.rightMargin;
int right = left + dividerDrawable.getIntrinsicWidth();
dividerDrawable.setBounds(left, top, right, bottom);
dividerDrawable.draw(c);
}
}
private void drawHorizontalDivider(Canvas c, RecyclerView parent) {
int left = parent.getLeft() + parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
for (int i = 0; i < parent.getChildCount(); i++) {
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
int top = childView.getBottom() + params.topMargin + Math.round(ViewCompat.getTranslationY(childView));
int bottom = top + dividerDrawable.getIntrinsicHeight();
dividerDrawable.setBounds(left, top, right, bottom);
dividerDrawable.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
outRect.set(0, 0, dividerDrawable.getIntrinsicWidth(), 0);
} else if (mOrientation == LinearLayoutManager.VERTICAL) {
outRect.set(0, 0, 0, dividerDrawable.getIntrinsicHeight());
}
}
}
首先我们来看getItemOffsets方法,这个方法的意思就是需要我们返回一个Rect的偏移区域来展现divider,outRect.set(left,top,right,bottom);设置左上右下的偏移区域;
再来看onDraw方法,这个方法就是具体实现divider分隔线的地方了,循环遍历所有子view,通过计算left,top,right,bottom来确定每一条分隔线的位置。
如果我们需要添加条目,需要在数据集合中添加一条数据,并且调用方法notifyItemInserted(position);
如果我们需要删除一个条目,需要在数据集合中删除一条数据,并且调用方法notifyItemRemoved(position);
添加删除条目时如需要一些动画效果可以在recyclerview中添加Animator,这里我们添加一个默认的Animator,rv.setItemAnimator(new DefaultItemAnimator());
下面我们再来看一下布局变换如何实现
public boolean onOptionsItemSelected(MenuItem item) {
rv.removeItemDecoration(dividerItemDecoration);
switch (item.getItemId()) {
case R.id.action_LinearLayoutManager_HORIZATOAL:
xyAdapter.setStaggerLayout(false);
dividerItemDecoration = new DividerItemDecoration(this,LinearLayoutManager.HORIZONTAL);
rv.addItemDecoration(dividerItemDecoration);
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
break;
case R.id.action_LinearLayoutManager_VERTICAL:
xyAdapter.setStaggerLayout(false);
dividerItemDecoration = new DividerItemDecoration(this,LinearLayoutManager.VERTICAL);
rv.addItemDecoration(dividerItemDecoration);
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
break;
case R.id.action_GridLayoutManager:
xyAdapter.setStaggerLayout(false);
dividerItemDecoration = new GridItemDecoration(this);
rv.addItemDecoration(dividerItemDecoration);
rv.setLayoutManager(new GridLayoutManager(this, 3));
break;
case R.id.action_StaagerLayoutManager:
xyAdapter.setStaggerLayout(true);
rv.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
break;
}
return super.onOptionsItemSelected(item);
}
效果图:
最后奉上源码地址:
XYMaterialDesign