Android漸變研究

jopen 8年前發布 | 32K 次閱讀 安卓開發 Android開發 移動開發

下面介紹一個android實現漸變的方式

GradientDrawable

用GradientDrawable實現漸變可以通過xml或者代碼實現,xml實現需要在drawable下建立xml文件,在 標簽下建立 標簽。

例如gradlient_background.xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <gradient android:startColor="#aa000000"
                      android:endColor="@android:color/transparent"
                      android:angle="90"
                />
        </shape>
    </item>
</selector>

設置方法如下:

<RelativeLayout
    xmlns:android="

<LinearLayout
    android:id="@+id/layout_bottom"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:layout_alignParentBottom="true"
    android:background="@drawable/gradlient_background"
    android:orientation="horizontal"
    />

</RelativeLayout></pre>

效果圖如下:

Android漸變研究

上面的例子中我們在gradient標簽中設置了startColor,endColor,angle用來表示開始結束的顏色和變化方向。 gradient標簽的所有屬性說明如下:

android:angle:(Integer) 漸變的角度,線性漸變時才有效,必須是45的倍數,0表示從左到右,90表示從下到上

android:centerX:(Float)漸變中心的相對X坐標,放射漸變時才有效,在0.0到1.0之間,默認為0.5,表示在正中間

android:centerY:(Float)漸變中心的相對X坐標,放射漸變時才有效,在0.0到1.0之間,默認為0.5,表示在正中間

android:centerColor :(Color)中間點的色值

android:endColor : (Color)結束的色值

android:startColor:(Color)開始的色值。

android:gradientRadius:(Float)漸變的半徑,只有在android:type="radial"的時候有效。

android:type :有三種類型 "linear" 線性漸變, "radial" 放射漸變,設置該項時,android:gradientRadius也必須設置 "sweep" 掃描性漸變

android:useLevel : 如果為true,將被當成LevelListDrawable使用。

除了用xml設置,還可以在編碼中設置, 標簽對應的類是GradientDrawable,GradientDrawable是Drawable的子類。 代碼如下:

public class TestActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(new SampleView(this));
}

private static class SampleView extends View {
    private Rect mRect;
    private GradientDrawable mDrawable;

    public SampleView(Context context) {
        super(context);
        setFocusable(true);

        mRect = new Rect(0, 0, 300, 300);
        mDrawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR,
                new int[] { 0xaa000000,
                        0xFFFFFFFF });
        mDrawable.setShape(GradientDrawable.RECTANGLE);
        mDrawable.setGradientRadius((float)(Math.sqrt(2) * 60));
    }

    static void setCornerRadii(GradientDrawable drawable, float r0,
                               float r1, float r2, float r3) {
        drawable.setCornerRadii(new float[] { r0, r0, r1, r1,
                r2, r2, r3, r3 });
    }

    @Override protected void onDraw(Canvas canvas) {

        mDrawable.setBounds(mRect);

        float r = 16;

        canvas.save();
        canvas.translate(10, 10);
        mDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
        setCornerRadii(mDrawable, r, r, 0, 0);
        mDrawable.draw(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(10 + mRect.width() + 10, 10);
        mDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);
        setCornerRadii(mDrawable, 0, 0, r, r);
        mDrawable.draw(canvas);
        canvas.restore();

        canvas.translate(0, mRect.height() + 10);

        canvas.save();
        canvas.translate(10, 10);
        mDrawable.setGradientType(GradientDrawable.SWEEP_GRADIENT);
        setCornerRadii(mDrawable, 0, r, r, 0);
        mDrawable.draw(canvas);
        canvas.restore();

    }
}

}</pre>

效果如下圖:

Android漸變研究

可以看到,代碼設置和xml設置大同小異,注意實例化的操作:

public GradientDrawable(Orientation orientation, int[] colors) {
        this(new GradientState(orientation, colors));
    }

第一個參數是一個枚舉,表示漸變方向,這個用來相當于xml里面的angle,只不過angle是用45的倍數表示方向,而枚舉看上去更清楚了。

public enum Orientation {
        /** draw the gradient from the top to the bottom */
        TOP_BOTTOM,
        /** draw the gradient from the top-right to the bottom-left */
        TR_BL,
        /** draw the gradient from the right to the left */
        RIGHT_LEFT,
        /** draw the gradient from the bottom-right to the top-left */
        BR_TL,
        /** draw the gradient from the bottom to the top */
        BOTTOM_TOP,
        /** draw the gradient from the bottom-left to the top-right */
        BL_TR,
        /** draw the gradient from the left to the right */
        LEFT_RIGHT,
        /** draw the gradient from the top-left to the bottom-right */
        TL_BR,
    }

第二個參數是一個color數組,相當于startColor,endColor,centerColor,其中centerColor可以省略,但是至少要設置兩個顏色。 mDrawable.setGradientType可以設置三種type同xml一樣,分別是GradientDrawable.LINEAR_GRADIENT,GradientDrawable .RADIAL_GRADIENT,GradientDrawable.SWEEP_GRADIENT。

Shader類的子類

Shader類的子類創建允許使用多種固體顏色填充繪圖對象的Paint,功能不只是實現漸變填充。有三Shader是用來做漸變的: LinearGradient、RadialGradient和 SweepGradient. 看名字就知道這三種和上面的GradientDrawable的三種type是對應的。 只不過是用Shader實現了。

要在繪圖的時候使用一個Shader,可以使用setShader方法將其應用到一個Paint中,如下面的代碼所示:

Paint shaderPaint = new Paint();  
    shaderPaint.setShader(myLinearGradient);

使用這個Paint所繪制的任何東西都將使用你指定的Shader進行填充,而不是使用Paint本身的顏色進行填充。下面使用LinearGradient實現漸變,對于RadialGradient和 SweepGradient使用很類似。

public class TestActivity extends Activity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(new SampleView(this));
        }

        private static class SampleView extends View {
            private Paint mPaint;
            private Rect mRect;
            LinearGradient lg1 ;
            LinearGradient lg2 ;
            LinearGradient lg3 ;

            public SampleView(Context context) {
                super(context);
                setFocusable(true);

                mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
                mRect = new Rect(0, 0, 300, 300);

                 lg1 = new LinearGradient(0,0,150,150, Color.TRANSPARENT,Color.BLACK,
                        Shader.TileMode.MIRROR);
                 lg2 = new LinearGradient(0,0,150,150, Color.TRANSPARENT,Color.BLACK,
                        Shader.TileMode.CLAMP);
                 lg3 = new LinearGradient(0,0,150,150, Color.TRANSPARENT,Color.BLACK,
                        Shader.TileMode.REPEAT);

            }

            @Override protected void onDraw(Canvas canvas) {
                canvas.save();
                canvas.translate(10, 10);
                mPaint.setShader(lg1) ;
                canvas.drawRect(mRect,mPaint);
                canvas.restore();

                canvas.save();
                canvas.translate(10 + mRect.width() + 10, 10);
                mPaint.setShader(lg2) ;
                canvas.drawRect(mRect,mPaint);
                canvas.restore();

                canvas.save();
                canvas.translate(10,10 + mRect.height() + 10);
                mPaint.setShader(lg3) ;
                canvas.drawRect(mRect,mPaint);
                canvas.restore();
            }
        }
    }</pre> <p>效果如下:</p>

Android漸變研究

上面的例子使用了三種Shader TileModes,如果Shader畫刷所定義的區域比要填充的區域小,那么TileMode將會決定如何處理剩余的區域:

MIRROR 在水平和垂直方向上拉伸Shader圖像,這樣每一個圖像就都能與上一個縫合了。

CLAMP 使用Shader的邊界顏色來填充剩余的空間。

REPEAT 在水平和垂直方向上重復Shader圖像,但不拉伸它。

LinearGradient有兩種方式實例化:

LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)

他們的不同之處為參數中第一種方法可以用顏色數組,和位置來實現更細膩的過渡效果, 比如顏色采樣int[] colors數組中存放20種顏色,則漸變將會逐一處理。而第二種方法參數僅為起初顏色color0和最終顏色color1。

自定義漸變

可以利用工具類重新計算LinearGradient的顏色參數,從而實現更柔和的漸變,仍然使用最開始的LinearLayout布局測試:

public class TestActivity extends Activity  {

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

         if(Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
             View bottom = findViewById(R.id.layout_bottom);
             bottom.setBackground(
                     ScrimUtil.makeCubicGradientScrimDrawable(
                             0xaa000000, //顏色
                             8, //漸變層數
                             Gravity.BOTTOM)); //起始方向
         }
     }

 }</pre> <p>ScrimUtil代碼如下:</p>

public class ScrimUtil {

private ScrimUtil() {
}

/**
 * Creates an approximated cubic gradient using a multi-stop linear gradient. See
 * <a >this post</a> for more
 * details.
 */
public static Drawable makeCubicGradientScrimDrawable(int baseColor, int numStops, int gravity) {
    numStops = Math.max(numStops, 2);

    PaintDrawable paintDrawable = new PaintDrawable();
    paintDrawable.setShape(new RectShape());

    final int[] stopColors = new int[numStops];

    int red = Color.red(baseColor);
    int green = Color.green(baseColor);
    int blue = Color.blue(baseColor);
    int alpha = Color.alpha(baseColor);

    for (int i = 0; i < numStops; i++) {
        float x = i * 1f / (numStops - 1);
        float opacity = constrain(0, 1, (float) Math.pow(x, 3));
        stopColors[i] = Color.argb((int) (alpha * opacity), red, green, blue);
    }

    final float x0, x1, y0, y1;
    switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
        case Gravity.LEFT:  x0 = 1; x1 = 0; break;
        case Gravity.RIGHT: x0 = 0; x1 = 1; break;
        default:            x0 = 0; x1 = 0; break;
    }
    switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
        case Gravity.TOP:    y0 = 1; y1 = 0; break;
        case Gravity.BOTTOM: y0 = 0; y1 = 1; break;
        default:             y0 = 0; y1 = 0; break;
    }

    paintDrawable.setShaderFactory(new ShapeDrawable.ShaderFactory() {
        @Override
        public Shader resize(int width, int height) {
            LinearGradient linearGradient = new LinearGradient(
                    width * x0,
                    height * y0,
                    width * x1,
                    height * y1,
                    stopColors, null,
                    Shader.TileMode.CLAMP);
            return linearGradient;
        }
    });

    return paintDrawable;
}

private  static float constrain(float min, float max, float v) {
    return Math.max(min, Math.min(max, v));
}

}</pre>

最終效果:

Android漸變研究

使用屬性動畫實現動態漸變

使用ArgbEvaluator.evaluate(floatfraction, Object startValue, Object endValue); 可以實現顏色動態漸變,使用一個自定義view來測試:

public class TestLayout extends View {

public TestLayout(Context context) {
    super(context);
    init(null, 0);
}
public TestLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(attrs, 0);
}
public TestLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(attrs, defStyle);
}

@SuppressWarnings("deprecation")
private void init(AttributeSet attrs, int defStyle) {
}


/**
 * =============================================================================================
 * The Animator methods
 * =============================================================================================
 */
/**
 * 開始背景動畫(此處為屬性動畫)
 */
void startBackgroundAnimator(){
    /*
     *參數解釋:
     *target:設置屬性動畫的目標類,此處是當前自定義view所以使用this
     *propertyName:屬性名稱。(要對View的那個屬性執行動畫操作)
     *values數組:根據時間的推移動畫將根據數組的內容進行改變
     */
    ValueAnimator anim = ObjectAnimator.ofInt(this, "backgroundColor", Color.RED,Color.BLUE,Color.GRAY,Color.GREEN);
    //動畫持續時間為3秒
    anim.setDuration(3000);
    /*
     * ArgbEvaluator:這種評估者可以用來執行類型之間的插值整數值代表ARGB顏色。
     * FloatEvaluator:這種評估者可以用來執行浮點值之間的插值。
     * IntEvaluator:這種評估者可以用來執行類型int值之間的插值。
     * RectEvaluator:這種評估者可以用來執行類型之間的插值矩形值。
     *
     * 由于本例是改變View的backgroundColor屬性的背景顏色所以此處使用ArgbEvaluator
     */
    anim.setEvaluator(new ArgbEvaluator());
    //設置動畫重復次數,此處設置無限重復
    anim.setRepeatCount(ValueAnimator.INFINITE);
    //設置重復模式
    anim.setRepeatMode(ValueAnimator.REVERSE);
    //開啟動畫
    anim.start();
}

}</pre>

布局如下:

<RelativeLayout
              xmlns:android="

          <com.mxn.soul.demo.TestLayout
              android:id="@+id/layout_bottom"
              android:layout_width="match_parent"
              android:layout_height="300dp"
              android:layout_alignParentBottom="true"
              android:orientation="horizontal"
              />

      </RelativeLayout></pre> <p>在activity中調用:</p>

public class TestActivity extends Activity  {

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

    TestLayout bottom = (TestLayout) findViewById(R.id.layout_bottom);
    bottom.startBackgroundAnimator() ;
}

}</pre>

效果如下:

Android漸變研究

</div>

來自: http://souly.cn/技術博文/2015/12/23/android漸變研究/

</code></code></code></code></code></code></code></code></code></code></code></code></code>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!