Material Design的风格学习
android 5.0 使用Material Design风格主题
分别是:
@android:style/Theme.Material (dark version)
@android:style/Theme.Material.Light (light version)
@android:style/Theme.Material.Light.DarkActionBar
我们可以使用这三个theme来定义我们自己的theme
<resources>
<style name="AppTheme" parent="android:Theme.Material">
<!-- your app branding color for the app bar -->
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorAccent">@color/accent</item>
</style>
</resources>
修改每个位置的颜色,每个位置的名字:
在低版本使用Material Design 风格
加入supprot library
dependencies {
compile 'com.android.support:appcompat-v7:+'
compile 'com.android.support:cardview-v7:+'
compile 'com.android.support:recyclerview-v7:+'
}
将上面的appTheme style 放到res/values-v21/style.xml, 再res/values/style.xml增加一个AppTheme.
<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
<!-- customize the color palette -->
<item name="colorPrimary">@color/material_blue_500</item>
<item name="colorPrimaryDark">@color/material_blue_700</item>
<item name="colorAccent">@color/material_green_A200</item>
</style>
这是主题的沉浸式状态栏。
创建列表和卡片
卡片布局是Material Design 的另外一个组成部分
在引用时需要引入依赖包
dependencies {
compile 'com.android.support:appcompat-v7:+'
compile 'com.android.support:cardview-v7:+'
compile 'com.android.support:recyclerview-v7:+'
}
创建list
recyclerView组件是一个更加灵活的listview, 这个组件时一个显示大数据集的容器,
可以有效的滚动,保持显示一定数量的视图.
使用RecyclerView组件,当你有数据集,并且数据集的元素在运
行时根据用户的操作或者网络事件改变。
RecylerView类简化大数据集的显示和处理,通过提供布局管理者控制元素定位.
在通用的元素上操作上显示默认的动画,比如移除和增加元素。
使用RecyclerView组件,你需要指定一个Adapter和布局管理器,
创建一个Adapter继承RecyclerView.Adapter类,具体的实现细节要根据数据集合视图的类型变化,
具体信息,看下面的例子。
一个布局管理器定位Item视图在RecyclerView中,决定什么时候去回收它当他不再可见时。当重用(或者回收)一个视图时,布局管理器可能会请求适配器(Adapter)去替换子视图中的内容用不同的内容。通过这种方式回收重用视图,可以减少view的创建和避免更多的findViewById(),从而提高性能。
recyclerview 提供了以下内建的布局管理器:
- LinearLayoutManager 显示Item 在一个水平或者垂直的滚动列表中。
- GridLayoutManager 显示Item 作为网格布局。
- StaggeredGridLayoutManager 显示Item在交错的网格布局
也可以自己通过继承RecyclerView.LayoutManager类来创建一个自定义的布局管理器。
动画
RecyclerView默认情况下就有动画,在删除或者增加Ite的时候.
如果需要自定义动画,继承RecyclerView.ItemAnimator, 并且使用RecyclerView.setItemAnimator()
来将动画设置到我们的视图中。
下面来看例子:
在xml布局增加一个RecyclerView.
<android.supporv7.widget.RecycleView android:id="@+id/my_recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent"/>
然后在java代码中使用,附加adapter和数据就行了。
public class MyActivity extends Activity { private RecyclerView mRecyclerView; private RecyclerView.Adapter mAdapter; private RecyclerView.LayoutManager mLayoutManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.my_activity); mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView mRecyclerView.setHasFixedSize(true); // use a linear layout manager mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); // specify an adapter (see also next example) mAdapter = new MyAdapter(myDataset); mRecyclerView.setAdapter(mAdapter); } ... }
adapter提供访问数据集中item,创建试图映射到数据上,并且替换布局的内容数据用新的item:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private String[] mDataset; // Provide a reference to the views for each data item // Complex data items may need more than one view per item, and // you provide access to all the views for a data item in a view holder public static class ViewHolder extends RecyclerView.ViewHolder { // each data item is just a string in this case public TextView mTextView; public ViewHolder(TextView v) { super(v); mTextView = v; } } // Provide a suitable constructor (depends on the kind of dataset) public MyAdapter(String[] myDataset) { mDataset = myDataset; } // Create new views (invoked by the layout manager) @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.my_text_view, parent, false); // set the view's size, margins, paddings and layout parameters ... ViewHolder vh = new ViewHolder(v); return vh; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element holder.mTextView.setText(mDataset[position]); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return mDataset.length; } }
创建Card
CardView继承FrameLayout类,通过它可以显示信息在卡片内部,并且在不同的平台上有统一的样式
CardVIew组件可以有阴影和圆角。
创建有阴影的Card,使用card_view:cardElevati属性。
设置圆角的半径:在布局文件里,card_view:cardCornerRadius;在java里,使用CardView.setRadius
而背景颜色使用card_view:cardBackgroundColor。
在布局文件里包含CardView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
... >
<!-- A CardView that contains a TextView -->
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
card_view:cardCornerRadius="4dp">
<TextView
android:id="@+id/info_text"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v7.widget.CardView>
</LinearLayout>
图片实例:
通常是把RecyclerView , cardView 写在一起。
定义阴影和剪裁视图
无法向下兼容,只可以在5.0及以上实现。
视图的高度(elevation),通过Z属性表现.
为视图分配高度
一个View的Z值有两部分组成,elevation(高度)和translation(平移)
elevation是一个静态部分, translation用于动画。
不同高度的视图的阴影
在布局文件设置elevation: android:elevation 在代码里使用:View.setElevation()
设置一个视图的平移,使用View.setTranslationZ().
新的方法ViewPropertyAnimator.z()和ViewPropertyAnimator.translationZ() 更容易实现变动视图的平移。
自定义视图阴影和轮廓
视图的背景边界决定了阴影的默认形状,轮廓(outlines)代表了图形的对象的外形状,
并确定了对触摸反馈的波纹。
这个视图,定义一个背景Drawable:
背景是一个圆角矩形myrect.xml:
!— res/drawable/myrect.xml —>
当这个背景drawable做为视图的轮廓时,视图投射出圆角阴影。
提供一个自定义的轮廓,可以覆盖默认视图阴影的形状。
在自己的代码里自定义一个轮廓:
- 继承ViewOutlineProvider
- 重写getOutline()方法
- 在视图里面设置轮廓,使用View.setOutlineProvider()方法。
创建椭圆和圆角矩形轮廓使用Outline中的方法。
视图默认的outline provider 会根据视图的背景来生成轮廓。
可以设置视图的outline prioder为 null ,来阻止投射阴影。
剪裁视图
裁剪视图,更容易改变视图的形状。
常用的方法:
- View.setClipToOutLine()方法裁剪一个视图的轮廓。(android:clipToOutline)
- Outline.canClip()方法检测是否支持被裁剪。
- View.setClipToOutline().
总结
设置阴影很简单:
- 设置elevation值
- 添加背景或是设置一个outline .
RecyclerView详解
RecyclerVIew的特点:你可以通过设置LayoutManger来快速实现listView、gridView、瀑布流的效果
而且还可以横向纵向显示,添加动画也比较方面,自带itemAnimation,可以设置加载和移除时的动画
item的动画实现
只需要调用以下几个函数来增删item即可出现默认动画。
notifyItemChanged(int)
notifyItemInserted(int)
notifyIteemRemoved(int)
notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)
也可以看开源库: [recyclerview-animators]{https://github.com/wasabeef/recyclerview-animators}
首先是布局文件里加入 RecyclerView
然后在activity中
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
//创建一个线性布局管理器
LinearLayoutManger layoutManger = new LinearLayoutManger(this);
//设置布局管理器,线性显示
recyclerView.setLayoutManger(layoutManger);
//recyclerView.setLayoutManger(new GridLayoutManger(this,2));//这里线性宫格显示,
//recyclerView.setLayoutManger(new StaggeredGridLayoutManger(2, orientationHelper.VERTICAL));//类似瀑布流
//创建数据集
String[] dataset = new String[100];
for (int i=0; i<dataset.length; i++) {
dataset[i] = "item" + i;
}
//创建Adapter ,并指定数据集
MyAdapter adapter = new MyAdapter(dataset);
//设置adapter
recyclerView.setAdapter(adapter);
recyclerView首先的一个特点就是将layout抽象成一个LayoutManger, recyclerView不负责
子view的布局,自定义LayoutManger来实现不同的布局效果,目前只有LinearLayoutManger。
adapter是怎么实现
public class MyAdapter extends REcyclerView.Adapter<MyAdapter.ViewHolder> {
//数据集
private String[] mDataset;
public MyAdapter(String[] dataset) {
super();
mDataset = dataset;
}
//必须重写的三个方法
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
//在这里设item布局
return holder;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
//绑定数据到ViewHolder上
}
@Override
public int getItemCount() {
return mDataset.length;
}
public static class ViewHolder extends RecyclerView.ViewHolder{
public ViewHolder (View view) {
super(view);
//
}
}
}
recyclerView的另外一个特点时标准化了ViewHolder,编写adapter面向的是ViewHolder而不在view,
复用的逻辑被封装了,更简单。
实现不同的效果,重要在于adapter,写一个复杂点的adapter
public class MultipleItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static enum ITEM_TYPE {
ITEM_TYPE_IMAGE,
ITEM_TYPE_TEXT
}
private final LayoutInflater mLayoutInflater;
private final Context mContext;
private String[] mTitles;
public MultipleItemAdapter (Context context) {
mTitles = context.getResources().getStringArray(R.array.titles);
mCOntext = context;
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
if (viewType == ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal()){
return new ImageViewHolder(mLayoutInflater.inflater(R.layout.item_image,parent,false));
} else {
return new TextViewHolder(mLayoutInflater.inflater(R.layout.item_text,parent,false));
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof TextViewHolder) {
((TextViewHolder) holder).mTextView.setText(mTiles[position]);
} else if(holder instanceof ImageViewHolder) {
((ImageViewHolder) holder).mTextView.setText(mTiles[position]);
}
}
@Override
public int getItemViewType(int position) {
return position % 2 == 0 ? ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal() : ITEM_TYPE
.ITEM_TYPE_TEXT.ordinal();
}
public static class TextViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.text_view)
TextView mTextView;
TextViewHolder(View v) {
super(view);
ButterKnife.inject(this, view);
view.setOnclickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TextViewHolder", "onClick--> position = " + getPosition());
}
});
}
}
public static class ImageViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.text_view)
TextView mTextView;
@InjectView(R.id.image_view)
ImageView mImageView;
TextView mTextView;
ImageViewHolder(View v) {
super(view);
ButterKnife.inject(this, view);
view.setOnclickListener(new View.OnClickListener() {
@Overrid
public void onClick(View v) {
Log.d("TextViewHolder", "onClick--> position = " + getPosition());
}
});
}
}
}
waiting
chenzhao@hustiunique.com