Material Design 控件(三)
TabLayout
-
在Gradle文件中的dependency中添加'compile 'com.android.support:design:22.2.1'依赖。
-
填写布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_tab_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.design.widget.TabLayout android:id="@+id/tab" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="scrollable" /> <android.support.v4.view.ViewPager android:id="@+id/vp" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
-
编写ViewPager 里面的Fragment 布局文件 fragment_tablayout.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Fragment" android:textSize="35sp"/> </RelativeLayout>
-
编写 fragment
public class TabFragment extends Fragment { public static final String PAGE = "page"; private int mPage; public static TabFragment newInstance(int page) { Bundle args = new Bundle(); args.putInt(PAGE, page); TabFragment fragment = new TabFragment(); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPage = getArguments().getInt(PAGE); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_tablayout, container, false); TextView textView = (TextView) view.findViewById(R.id.tv); textView.setText("第" + mPage + "页"); return view; } }
-
编写ViewPager的适配器
public class MyFragmentPagerAdapter extends FragmentPagerAdapter { public final int COUNT = 10; private String[] titles = new String[]{"Tab1", "Tab2", "Tab3","Tab4","Tab5","Tab6","Tab7","Tab8","Tab9","Tab10"}; private Context context; private int[] imageResId = {R.drawable.button_emoji_press, R.drawable.button_mic_press, R.drawable.ic_comment_love_yellow}; public MyFragmentPagerAdapter(FragmentManager fm, Context context) { super(fm); this.context = context; } @Override public Fragment getItem(int position) { return TabFragment.newInstance(position + 1); } @Override public int getCount() { return COUNT; } @Override public CharSequence getPageTitle(int position) { return titles[position]; } }
-
编写TabLayoutActivity
public class TabLayoutActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tab_layout); ViewPager viewPager = (ViewPager) findViewById(R.id.vp); MyFragmentPagerAdapter adapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), this); viewPager.setAdapter(adapter); TabLayout tabLayout = (TabLayout) findViewById(R.id.tab); //必须在 viewPager.setAdapter(adapter); 之后调用 tabLayout.setupWithViewPager(viewPager); } }
上述代码就把 TabLayout 和 ViewPager 连接起来了。
同时你也可不和ViewPager 连用。
通过TabLayout的addTab()方法添加新构建的Tab实例到TabLayout中;
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab);
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
我们也可以自己为这些Tab增加点击事件
tabLayout.setOnTabSelectedListener(newTabLayout.OnTabSelectedListener() {
@Override
public voidonTabSelected(TabLayout.Tab tab) {
//选中了tab的逻辑
}
@Override
public voidonTabUnselected(TabLayout.Tab tab) {
//未选中tab的逻辑
}
@Override
public voidonTabReselected(TabLayout.Tab tab) {
//再次选中tab的逻辑
}
});
如果setOnTabSelectedListener 设置了,那么如果你的Tab要和ViewPage绑定的,那么tabLayout.setupWithViewPager(viewPager); 要放在setOnTabSelectedListener之前,并且要在onTabSelected方法中添加更换
viewPager.setCurrentItem(tab.getPosition());的操作,否则tab点击后viewPage不会改变。原因如下:
从TabLayout源码中可以看到
public void setupWithViewPager(@Nullable final ViewPager viewPager) {
.
......
// Now we'll add a tab selected listener to set ViewPager's current item
setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager));
.....
}
他会重新setOnTabSelectedListener,而我们外面设置的setOnTabSelectedListener会被替换。
再从ViewPagerOnTabSelectedListener源码中可以看出:
public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
private final ViewPager mViewPager;
public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
mViewPager = viewPager;
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// No-op
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// No-op
}
}
源码中ViewPagerOnTabSelectedListener也是继承于OnTabSelectedListener,而只在onTabSelected里面做了切换页面的操作。
Tablayout的一些设置
TabGravity 与 TabMode
TabGravity:有GRAVITY_CENTER 和 GRAVITY_FILL两种效果。
GRAVITY_CENTER : 居中
GRAVITY_FILL :尽可能的填充(注意,GRAVITY_FILL需要和MODE_FIXED一起使用才有效果)
TabMode:有 MODE_FIXED 和 MODE_SCROLLABLE。
MODE_FIXED:固定tabs,并同时显示所有的tabs。
那么就全部显示出来,而内容却看不到了,不能移动。
MODE_SCROLLABLE:可滚动tabs,显示一部分tabs,在这个模式下能包含长标签和大量的tabs,最好用于用户不需要直接比较tabs。
上图就是 app:tabMode="scrollable" 属性
设置这些属性有两种办法。
-
代码:
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); tabLayout.setTabMode(TabLayout.MODE_FIXED);
-
XML布局文件
<android.support.design.widget.TabLayout android:id="@+id/tabLayout" app:tabGravity="fill" app:tabMode="fixed" android:layout_width="match_parent" android:layout_height="wrap_content" />
Tab 的自定义布局
在MyFragmentPagerAdapter中添加getTabView(int position) ,
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
....
public View getTabView(int position){
View view = LayoutInflater.from(context).inflate(R.layout.tab_item, null);
TextView tv= (TextView) view.findViewById(R.id.tv);
tv.setText(titles[position]);
ImageView img = (ImageView) view.findViewById(R.id.iv);
img.setImageResource(R.drawable.ic_comment_love_yellow);
return view;
}
}
然后在onCreate中加入:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_layout);
ViewPager viewPager = (ViewPager) findViewById(R.id.vp);
MyFragmentPagerAdapter adapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),
this);
viewPager.setAdapter(adapter);
//TabLayout
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab);
//必须在 viewPager.setAdapter(adapter); 之后调用
tabLayout.setupWithViewPager(viewPager);
//使用自定义布局
for (int i = 0; i < tabLayout.getTabCount(); i++) {
TabLayout.Tab tabAt = tabLayout.getTabAt(i);
tabAt.setCustomView(adapter.getTabView(i));
}
}
效果如图所示:
同样我们可以用选择器来设置选中的效果。
TextView中设置颜色:
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="asd"
android:textColor="@color/tab_text_select"/>
字体颜色选择器:tab_text_select.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_selected="true" android:color="#ff0000"></item>
<item android:state_selected="false" android:color="#3e0000ff"></item>
</selector>
图标选择器:tab_select.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
android:drawable="@drawable/ic_right_green"/>
<item android:state_selected="false"
android:drawable="@drawable/ic_comment_love_yellow"/>
</selector>
然后在代码中的
ImageView img = (ImageView) view.findViewById(R.id.iv);
img.setImageResource(R.drawable.ic_comment_love_yellow);
改成
ImageView img = (ImageView) view.findViewById(R.id.iv);
img.setImageResource(R.drawable.tab_select);
一些属性介绍
-
改变选中字体的颜色
app:tabSelectedTextColor=""
-
改变未选中字体的颜色
app:tabTextColor=""
-
改变指示器下标的颜色
app:tabIndicatorColor=""
-
改变整个TabLayout的颜色
app:tabBackground="color"
-
改变TabLayout内部字体大小
字体有点小了,于是想找方法把这个字变得大一点,却没有直接变大的方法,可是找到了这个:
app:tabTextAppearance=""
-
改变指示器下标的高度
app:tabIndicatorHeight=""
-
添加图标
tabLayout.addTab(tabLayout.newTab().setText("Tab 1").setIcon(R.mipmap.ic_launcher));
-
Tab内部的子控件的Padding
app:tabPadding="xxdp" app:tabPaddingTop="xxdp" app:tabPaddingStart="xxdp" app:tabPaddingEnd="xxdp" app:tabPaddingBottom="xxdp"
-
整个TabLayout的Padding
app:paddingEnd="xxdp" app:paddingStart="xxdp"
-
tab宽度
app:tabMaxWidth="xxdp" 最大宽度 app:tabMinWidth="xxdp" 最小宽度
-
TabLayout开始位置的偏移量:
app:tabContentStart="100dp"
Palette
Palette 可以从一张图片中提取颜色 , 提取出的颜色可以设成别的控件的颜色 ,使整体风格保持一致。
使用前 引入 compile 'com.android.support:palette-v7:21.0.0'
下面的例子用来更换一下 Tab 和 状态栏 的颜色.
-
fragment_tablayout.xml 添加ImageView 控件
<ImageView android:id="@+id/iv" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop"/>
-
修改TabFragment中的onCreateView方法
//背景的ID public static int bitmapID[] = {R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d,R.drawable.e,R.drawable.f,R.drawable.g,R.drawable.h} ; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_tablayout, container, false); TextView textView = (TextView) view.findViewById(R.id.tv); textView.setText("第" + mPage + "页"); //设置ImageView的背景 ImageView iv = (ImageView) view.findViewById(R.id.iv); iv.setBackgroundResource(bitmapID[mPage-1]); return view; }
-
修改TabLayoutActivity,处理变色。
private TabLayout tabLayout; private ViewPager viewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tab_layout); viewPager = (ViewPager) findViewById(R.id.vp); MyFragmentPagerAdapter adapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), this); viewPager.setAdapter(adapter); tabLayout = (TabLayout) findViewById(R.id.tab); //必须在 viewPager.setAdapter(adapter); 之后调用 tabLayout.setupWithViewPager(viewPager); //设置选择时的监听,更改颜色 tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { viewPager.setCurrentItem(tab.getPosition()); //修改颜色 colorChange(tab.getPosition()); } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { } }); } //界面颜色的更改 private void colorChange(int position) { // 用来提取颜色的Bitmap Bitmap bitmap = BitmapFactory.decodeResource(getResources(), TabFragment.bitmapID[position]); // Palette的部分 Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() { //提取完之后的回调方法 @Override public void onGenerated(Palette palette) { Palette.Swatch vibrant = palette.getVibrantSwatch(); Palette.Swatch darkVibrant = palette.getDarkVibrantSwatch(); tabLayout.setBackgroundColor(vibrant.getRgb()); tabLayout.setSelectedTabIndicatorColor(darkVibrant.getRgb()); if (android.os.Build.VERSION.SDK_INT >= 21) { Window window = getWindow(); // 很明显,这两个是新API才有的。 window.setStatusBarColor(colorBurn(vibrant.getRgb())); window.setNavigationBarColor(colorBurn(vibrant.getRgb())); } } }); } //颜色加深处理,用来更改状态的颜色 private int colorBurn(int RGBValues) { int alpha = RGBValues >> 24; int red = RGBValues >> 16 & 0xFF; int green = RGBValues >> 8 & 0xFF; int blue = RGBValues & 0xFF; red = (int) Math.floor(red * (1 - 0.1)); green = (int) Math.floor(green * (1 - 0.1)); blue = (int) Math.floor(blue * (1 - 0.1)); return Color.rgb(red, green, blue); }
colorBurn方法解释:
RGB的值,由alpha(透明度)、red(红)、green(绿)、blue(蓝)构成,
Android中我们一般使用它的16进制,
例如:"#FFAABBCC",最左边到最右每两个字母就是代表alpha(透明度)、
red(红)、green(绿)、blue(蓝)。每种颜色值占一个字节(8位),值域0~255
所以下面使用移位的方法可以得到每种颜色的值,然后每种颜色值减小一下,在合成RGB颜色,颜色就会看起来深一些了
效果如图所示:
Palette的属性介绍:
Palette可以提取的颜色如下:
* Vibrant (有活力的)
* Vibrant dark(有活力的 暗色)
* Vibrant light(有活力的 亮色)
* Muted (柔和的)
* Muted dark(柔和的 暗色)
* Muted light(柔和的 亮色)
1. 我们需要通过一个Bitmap对象来生成一个对应的Palette对象。
所以例子中使用了从资源中获取。
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
TabFragment.bitmapID[position]);
Palette 提供了四个静态方法用来生成Palette对象。
* Palette generate(Bitmap bitmap)
* Palette generate(Bitmap bitmap, int numColors)
* generateAsync(Bitmap bitmap, PaletteAsyncListener listener)
* generateAsync(Bitmap bitmap, int numColors, final PaletteAsyncListener listener)
不难看出,生成方法分为generate(同步)和generateAsync(异步)两种,如果图片过大使用generate方法,可能会阻塞主线程,我们更倾向于使用generateAsync的方法,其实内部就是创建了一个AsyncTask。generateAsync方法需要一个PaletteAsyncListener对象用于监听生成完毕的回调。除了必须的Bitmap参数外,还可以传入一个numColors参数指定颜色数,默认是 16。
2. 得到Palette对象后,就可以拿到提取到的颜色值
* Palette.getVibrantSwatch()
* Palette.getDarkVibrantSwatch()
* Palette.getLightVibrantSwatch()
* Palette.getMutedSwatch()
* Palette.getDarkMutedSwatch()
* Palette.getLightMutedSwatch()
3. 使用颜色,上面get方法中返回的是一个 Swatch 样本对象,这个样本对象是Palette的一个内部类,它提供了一些获取最终颜色的方法。
* getPopulation(): 样本中的像素数量
* getRgb(): 颜色的RBG值
* getHsl(): 颜色的HSL值
* getBodyTextColor(): 主体文字的颜色值
* getTitleTextColor(): 标题文字的颜色值
通过 getRgb() 可以得到最终的颜色值并应用到UI中。getBodyTextColor() 和 getTitleTextColor() 可以得到此颜色下文字适合的颜色,这样很方便我们设置文字的颜色,使文字看起来更加舒服。
下面例子只用来显示各种不同的颜色
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.f);
// 异步提取Bitmap颜色
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
// 提取完毕
// 有活力的颜色
Palette.Swatch vibrant = palette.getVibrantSwatch();
// 有活力的暗色
Palette.Swatch darkVibrant = palette.getDarkVibrantSwatch();
// 有活力的亮色
Palette.Swatch lightVibrant = palette.getLightVibrantSwatch();
// 柔和的颜色
Palette.Swatch muted = palette.getMutedSwatch();
// 柔和的暗色
Palette.Swatch darkMuted = palette.getDarkMutedSwatch();
// 柔和的亮色
Palette.Swatch lightMuted = palette.getLightMutedSwatch();
tv1.setBackgroundColor(vibrant.getRgb());
tv2.setBackgroundColor(darkVibrant.getRgb());
tv3.setBackgroundColor(lightVibrant.getRgb());
tv4.setBackgroundColor(muted.getRgb());
tv5.setBackgroundColor(darkMuted.getRgb());
tv6.setBackgroundColor(lightMuted.getRgb());
}
});
效果如下:
TextInputLayout
一般嵌套一个EditText,用来在输入内容后提示内容显示在外面,还具有空校验。
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:id="@+id/til_1"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="请输入账号"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:id="@+id/til_2"
android:layout_height="wrap_content">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="请输入密码"/>
</android.support.design.widget.TextInputLayout>
为EditText添加校验
til1 = (TextInputLayout) findViewById(R.id.til_1);
til1.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.length()<6) {
til1.setErrorEnabled(true);
til1.setError("账号长度应超过6位");
return;
} else {
til1.setErrorEnabled(false);
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
这里需要注意的是,TextInputLayout的颜色来自style中的colorAccent的颜色
效果如下: