Material Design 控件(三)

TabLayout

  1. 在Gradle文件中的dependency中添加'compile 'com.android.support:design:22.2.1'依赖。

  2. 填写布局文件:

     <?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>
    
  3. 编写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>
    
  4. 编写 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;
         }
     }
    
  5. 编写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];
         }
     }
    
  6. 编写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" 属性

设置这些属性有两种办法。

  1. 代码:

      tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
      tabLayout.setTabMode(TabLayout.MODE_FIXED);
    
  2. 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);

一些属性介绍

  1. 改变选中字体的颜色

     app:tabSelectedTextColor=""
    
  2. 改变未选中字体的颜色

     app:tabTextColor=""
    
  3. 改变指示器下标的颜色

     app:tabIndicatorColor=""
    
  4. 改变整个TabLayout的颜色

     app:tabBackground="color"
    
  5. 改变TabLayout内部字体大小

    字体有点小了,于是想找方法把这个字变得大一点,却没有直接变大的方法,可是找到了这个:

     app:tabTextAppearance=""
    
  6. 改变指示器下标的高度

     app:tabIndicatorHeight=""
    
  7. 添加图标

     tabLayout.addTab(tabLayout.newTab().setText("Tab 1").setIcon(R.mipmap.ic_launcher));
    
  8. Tab内部的子控件的Padding

     app:tabPadding="xxdp"
     
     app:tabPaddingTop="xxdp"
     
     app:tabPaddingStart="xxdp"
     
     app:tabPaddingEnd="xxdp"
     
     app:tabPaddingBottom="xxdp" 
    
  9. 整个TabLayout的Padding

     app:paddingEnd="xxdp"
    
     app:paddingStart="xxdp"
    
  10. tab宽度

    app:tabMaxWidth="xxdp" 最大宽度
    
    app:tabMinWidth="xxdp" 最小宽度
    
  11. TabLayout开始位置的偏移量:

    app:tabContentStart="100dp"
    

Palette

Palette 可以从一张图片中提取颜色 , 提取出的颜色可以设成别的控件的颜色 ,使整体风格保持一致。

使用前 引入 compile 'com.android.support:palette-v7:21.0.0'

下面的例子用来更换一下 Tab 和 状态栏 的颜色.

  1. fragment_tablayout.xml 添加ImageView 控件

     <ImageView
         android:id="@+id/iv"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:scaleType="centerCrop"/>
    
  2. 修改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;
     }
    
  3. 修改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的颜色

效果如下:

posted @ 2017-01-19 16:16  吃枣的事  阅读(244)  评论(0编辑  收藏  举报