Andorid tint属性及如何使用tint实现Selector效果

偶然接触到Android 控件的tint这一属性,之前一直没有用过,所以非常感兴趣,便花了点时间研究了下。

tint 译为着色。tint一般与tintMode配合使用。 同时还有backgroundTint和backgroundTintMode属性(很明显backgroundTint是针对背景色着色,通过实践也证实了backgroundTint只有在为控件设置了background属性才会生效。)

tintMode和backgroundTintMode有六种模式:
src_in
add
multiply
screen
src_atop
src_over

其中默认是src_in.

为了更直观的看出tint和backgroundTint的用法以及tint各种模式的区别,我们直接通过代码来看吧,否则再怎么说也是云里雾里。。。

首先MainActivity的代码很简单,就是显示activity_main.xml布局文件。如下:

package com.android.monkey.testtint;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

现在我们重点看看activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.android.monkey.testtint.MainActivity"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp">
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/star_on"/>
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/star_on"
            android:tint="#00ff00"
            android:tintMode="src_in"/>
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/star_on"
            android:tint="#00ff00"
            android:tintMode="add"/>
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/star_on"
            android:tint="#00ff00"
            android:tintMode="multiply"/>
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/star_on"
            android:tint="#00ff00"
            android:tintMode="screen"/>
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/star_on"
            android:tint="#00ff00"
            android:tintMode="src_atop"/>
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@android:drawable/star_on"
            android:tint="#00ff00"
            android:tintMode="src_over"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="原图"
            android:gravity="center"/>
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="src_in"
            android:gravity="center"/>
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="add"
            android:gravity="center"/>
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="multiply"
            android:gravity="center"/>
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="screen"
            android:gravity="center"/>
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="src_atop"
            android:gravity="center"/>
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="src_over"
            android:gravity="center"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="#ffffff"
            android:src="@android:drawable/star_on" />
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="#ffffff"
            android:backgroundTint="#0000ff"
            android:backgroundTintMode="src_in"
            android:src="@android:drawable/star_on" />
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:backgroundTintMode="add"
            android:background="#ffffff"
            android:backgroundTint="#0000ff"
            android:src="@android:drawable/star_on" />
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:backgroundTintMode="multiply"
            android:background="#ffffff"
            android:backgroundTint="#0000ff"
            android:src="@android:drawable/star_on" />
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:backgroundTintMode="screen"
            android:background="#ffffff"
            android:backgroundTint="#0000ff"
            android:src="@android:drawable/star_on" />
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:backgroundTintMode="src_atop"
            android:background="#ffffff"
            android:backgroundTint="#0000ff"
            android:src="@android:drawable/star_on" />
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:backgroundTintMode="src_over"
            android:background="#ffffff"
            android:backgroundTint="#0000ff"
            android:src="@android:drawable/star_on" />
    </LinearLayout>

</LinearLayout>

根布局是一个垂直的线性布局,里面有三个水平的线性布局。第一个水平线性布局使用的是tint和tintMode。第二个线性布局显示的是tintMode的名称,方便运行之后与效果对应起来看。第三个线性布局使用的是android:backgroundTint和android:backgroundTintMode。

在manifest.xml清单文件中我们将MainAcitivty的方向设置为水平,运行之后结果如下:

这里写图片描述

这样可以比较明显的看出各种TintMode对应的效果。

值得注意的是:
1.backgroundTint只有在设置了background才有效。
2.在Andrid 5.0以后使用才不会出现问题,如果是5.0以下的系统则需要通过support包提供的兼容控件来实现,这里不多说,感应趣的自己可以研究下。


通过tint这一属性我们只是用了一张图片便得到了不同的效果。很自然的我们会想到是否可以使用tint来实现Android中selector的效果,如果可以那么将对缩小APK的大小有帮助。通过实例证明这种方式可行的,只是在最开始遇到了很多坑。

1.使用selector的方式实现各种点击态的方式很简单,所以这里不做说明了,只是简单的贴下代码:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@mipmap/selected"/>
    <item android:drawable="@mipmap/normal"/>

</selector>

2.通过tint属性实现selector效果。这里我们使用ImageView来做说明。

首先看下color文件夹下color_tint.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:color="@color/color_ff0000"></item>
    <item android:color="@color/color_ff6c38"></item>
</selector>

其中:

    <color name="color_ff0000">#ff0000</color>
    <color name="color_ff6c38">#ff6c38</color>

在布局文件中显示一个有点击态的ImageView:

<ImageView
        android:layout_width="150px"
        android:layout_height="150px"
        android:src="@mipmap/ic_launcher"
        android:tint="@color/color_tint"
        android:clickable="true"/>

注意一定要将ImageView设置为可点击的状态,否则无论怎么样你都看不到效果。

运行程序后点击并没有效果出现我们想要的效果。然后想到通过代码来实现tint效果看行不行呢?

    //代码设置tint同时实现selector的功能
    private void addViewOne() {
        ImageView imageView = CreateImageView();
        Drawable drawable = 
            ContextCompat.getDrawable(this,R.mipmap.ic_launcher);
        //Drawable.ConstantState state = drawable.getConstantState();
        //Drawable wrapDrawable = DrawableCompat.wrap(
        // state==null?drawable:state.newDrawable()).mutate();
        Drawable wrapDrawable = DrawableCompat.wrap(drawable).mutate();
        wrapDrawable.setBounds(
            0,
            0,
            wrapDrawable.getIntrinsicWidth(),

        DrawableCompat.setTint(
            wrapDrawable,
            ContextCompat.getColor(this,R.color.color_tint));
        imageView.setImageDrawable(wrapDrawable);
        imageView.setClickable(true);//这一步很重要,否则点击无效果
        mLlContainer.addView(imageView);
    }

很遗憾,同样没有效果。

分析原因:在图片点击之后会执行drawable的onStateChange(int[] state)方法。这里我们对应的是BitmapDrawable,查看那个类的onStateChange(int[] state)方法,我猜测是在点击的时候并没有执行drawable的invalidateSelf()方法,从而并没有更新界面的UI显示。

后来尝试使用StateListDrawable来看是否能实现上述功能。在布局文件中的xml代码如下:

<ImageView
        android:layout_width="150px"
        android:layout_height="150px"
        android:src="@drawable/selector"
        android:tint="@color/color_tint"
        android:clickable="true"/>

其中selector.xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" 
          android:drawable="@mipmap/ic_launcher"/>
    <item android:drawable="@mipmap/ic_launcher"/>

</selector>

通过对比这里和我们第一次的xml代码会发现只有一处不同,那就是src属性不一样。这里src属性的selector文件就相当于StateListDrawable,而selector文件为什么要写两个一模一样的图片ic_launcher,那是因为如果不是每种状态都对应一个drawable文件,在状态改变的时候StateListDrawable不会更新UI。运行程序发现效果终于出来了。

上面演示的是xml如何实现,接着来看看java代码中如何实现:

    //使用StateListDrawble实现selector
    private void addViewTwo() {
        ImageView imageView = CreateImageView();
        Drawable drawable = 
            ContextCompat.getDrawable(this,R.mipmap.ic_launcher);
        int[][] states = new int[2][];
        states[0] = new int[]{android.R.attr.state_pressed};
        states[1] = new int[]{};

        StateListDrawable stateListDrawable = new StateListDrawable();
        stateListDrawable.addState(states[0],drawable);
        stateListDrawable.addState(states[1],drawable);

        int[] color = new int[]{
                ContextCompat.getColor(this,R.color.color_ff0000),
                ContextCompat.getColor(this,R.color.color_ff6c38)};

        ColorStateList colorStateList = new ColorStateList(states,color);

        Drawable.ConstantState state = 
            stateListDrawable.getConstantState();
        Drawable wrapDrawable = 
            DrawableCompat.wrap(state == null ? stateListDrawable :      state.newDrawable()).mutate();

        wrapDrawable.setTintList(colorStateList);

        imageView.setImageDrawable(wrapDrawable);
        imageView.setClickable(true);//这一步很重要,否则点击无效果
        mLlContainer.addView(imageView);
    }

通过代码也能顺利实现我们想要的效果。

这里有个问题就是如果我使用ImageButton的话使用第一种方式也能实现效果,目前还没有找到原因,贴下代码,知道的同学欢迎随时告知,灰常感谢:

<ImageButton
        android:layout_width="150px"
        android:layout_height="150px"
        android:src="@mipmap/ic_launcher"
        android:tint="@color/color_tint" />

但是此时如果使用android:backgroundTint也不行。

<ImageButton
        android:layout_width="150px"
        android:layout_height="150px"
        android:background="@mipmap/ic_launcher"
        android:backgroundTint="@color/color_tint" />

好了,遇到的坑都说得差不多了,贴下完整的代码吧,以供参考:
SecondActivity:

package com.android.monkey.testtint;

import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;

/**
 * Description:
 * Author: Monkey
 * Time: 2017/1/13 14:24
 */

public class SecondActivity extends AppCompatActivity {
    private LinearLayout mLlContainer;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        initView();
    }

    private void initView() {
        mLlContainer = (LinearLayout)findViewById(R.id.ll_container);
        addViewOne();
        addViewTwo();
    }
    public ImageButton CreateImageButton(){
        ImageButton imageButton = new ImageButton(this);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(150, 150);
        imageButton.setLayoutParams(lp);
        return imageButton;
    }
    public ImageView CreateImageView(){
        ImageView imageView = new ImageView(this);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(150, 150);
        imageView.setLayoutParams(lp);
        return imageView;
    }

    //代码设置tint同时实现selector的功能。(实现了tint,但是没有实现selector。BitmapDrawable实现不了)
    private void addViewOne() {
        ImageView imageView = CreateImageView();
        Drawable drawable = ContextCompat.getDrawable(this,R.mipmap.ic_launcher);
        //Drawable.ConstantState state = drawable.getConstantState();
        //Drawable wrapDrawable = DrawableCompat.wrap(
        // state==null?drawable:state.newDrawable()).mutate();
        Drawable wrapDrawable = DrawableCompat.wrap(drawable).mutate();
        wrapDrawable.setBounds(0,0,wrapDrawable.getIntrinsicWidth(),wrapDrawable.getIntrinsicHeight());
        DrawableCompat.setTint(wrapDrawable,ContextCompat.getColor(this,R.color.color_tint));
        imageView.setImageDrawable(wrapDrawable);
        imageView.setClickable(true);//这一步很重要,否则点击无效果
        mLlContainer.addView(imageView);
    }

    //使用StateListDrawble实现selector
    private void addViewTwo() {
        ImageView imageView = CreateImageView();
        Drawable drawable = ContextCompat.getDrawable(this,R.mipmap.ic_launcher);
        int[][] states = new int[2][];
        states[0] = new int[]{android.R.attr.state_pressed};
        states[1] = new int[]{};

        StateListDrawable stateListDrawable = new StateListDrawable();
        stateListDrawable.addState(states[0],drawable);
        stateListDrawable.addState(states[1],drawable);

        int[] color = new int[]{
                ContextCompat.getColor(this,R.color.color_ff0000),
                ContextCompat.getColor(this,R.color.color_ff6c38)};

        ColorStateList colorStateList = new ColorStateList(states,color);

        Drawable.ConstantState state = stateListDrawable.getConstantState();
        Drawable wrapDrawable = DrawableCompat.wrap(
                state == null ? stateListDrawable : state.newDrawable()).mutate();
        wrapDrawable.setTintList(colorStateList);
        imageView.setImageDrawable(wrapDrawable);
        imageView.setClickable(true);//这一步很重要,否则点击无效果
        mLlContainer.addView(imageView);
    }

}

activity_second.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ImageView
        android:layout_width="150px"
        android:layout_height="150px"
        android:src="@mipmap/ic_launcher"
        android:tint="@color/color_tint"
        android:clickable="true"/>
    <ImageView
        android:layout_width="150px"
        android:layout_height="150px"
        android:src="@drawable/selector"
        android:tint="@color/color_tint"
        android:clickable="true"/>

    <ImageButton
        android:layout_width="150px"
        android:layout_height="150px"
        android:background="@mipmap/ic_launcher"
        android:backgroundTint="@color/color_tint" />
    <ImageButton
        android:layout_width="150px"
        android:layout_height="150px"
        android:src="@mipmap/ic_launcher"
        android:tint="@color/color_tint" />
</LinearLayout>

上面用到的selector.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@mipmap/ic_launcher"/>
    <item android:drawable="@mipmap/ic_launcher"/>

</selector>

color文件夹下color_tint.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/color_ff0000"></item>
<item android:color="@color/color_ff6c38"></item>
</selector>

结果如下(加粗的表示能实现效果):
1.xml文件 使用ImageView BitmapDrawable
2.xml文件 使用ImageView StateListDrawable
3.xml文件 使用ImageButton background和backgroundTint
4.xml文件 使用ImageButton BitmapDrawable
5.java代码 使用ImageView BitmapDrawable
6.java代码 使用ImageView StateListDrawable
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值