第一,Drawable概念
鴻陽大神的博客,大家可以參考下
這篇博客也可以參考下
Drawable就是一個可畫的對象,表示一種可以在Canvas上進行繪製的抽象的概念,其可能是一張(BitmapDrawable),
也可能是一個圖形(ShapeDrawable),還有可能是一個圖層(LayerDrawable),我們根據畫圖的需求,創建相應的可畫對象,
就可以將這個可畫對象當作一塊“畫布(Canvas)”,在其上面操作可畫對象,並最終將這種可畫對象顯示在畫布上,有點類似於“內存畫布“。
ImageView imageView = new ImageView(this);
Drawable drawable = getResources().getDrawable(R.drawable.avft);
imageView.setImageDrawable(drawable);
Drawable 並不是一張圖片,而是繪製圖片到canvas的工具。
Drawable有三個重要的方法,第一個是draw();這是個抽象方法:public abstract void draw( Canvas canvas);這是個抽象方法,具體邏輯在子類中實現。
第二個:
/**
812 * Specify the level for the drawable. This allows a drawable to vary its
813 * imagery based on a continuous controller, for example to show progress
814 * or volume level.
815 *
816 * <p>If the new level you are supplying causes the appearance of the
817 * Drawable to change, then it is responsible for calling
818 * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
819 * true will be returned from this function.
820 *
821 * @param level The new level, from 0 (minimum) to 10000 (maximum).
822 *
823 * @return Returns true if this change in level has caused the appearance
824 * of the Drawable to change (hence requiring an invalidate), otherwise
825 * returns false.
826 */
827 public final boolean setLevel(@IntRange(from=0,to=10000) int level) {
828 if (mLevel != level) {
829 mLevel = level;
830 return onLevelChange(level);
831 }
832 return false;
833 }
834
官方的解釋翻譯過來就是:指定可繪製對象的級別。這允許可繪製對象基於連續控制器改變其圖像。例如顯示進度或音量級別。
可以理解爲設置圖層,值爲0~10000;當然了,有set方法,就有get方法
我們先看下代碼
package com.example.lsn5;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* @author writing
* @time 2019/12/14 16:13
* @note
*/
public class RevealDrawable extends Drawable {
private Drawable selectedImage;
private Drawable unSelectedImage;
public RevealDrawable(Drawable selectedImage, Drawable unSelectedImage) {
this.selectedImage = selectedImage;
this.unSelectedImage = unSelectedImage;
}
@Override
public void draw(@NonNull Canvas canvas) {
Rect bounds = getBounds();
Rect temp = new Rect();
Gravity.apply(Gravity.LEFT,bounds.width()/2,bounds.height(),bounds,temp);
canvas.save();
canvas.clipRect(temp);
selectedImage.draw(canvas);
canvas.restore();
Gravity.apply(Gravity.RIGHT,bounds.width()/2,bounds.height(),bounds,temp);
canvas.save();
canvas.clipRect(temp);
unSelectedImage.draw(canvas);
canvas.restore();
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
selectedImage.setBounds(bounds);
unSelectedImage.setBounds(bounds);
}
@Override
public int getIntrinsicHeight() {
return unSelectedImage.getIntrinsicHeight();
}
@Override
public int getIntrinsicWidth() {
return unSelectedImage.getIntrinsicWidth();
}
@Override
public void setAlpha(int i) {
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return 0;
}
}
核心代碼就是draw了,but,我們只需要一行~~~~setAlpha、setColorFilter、getOpacity、draw這幾個方法是必須實現的,不過除了draw以爲,其他都很簡單。getIntrinsicWidth、getIntrinsicHeight主要是爲了在View使用wrap_content的時候,提供一下尺寸,默認爲-1可不是我們希望的。setBounds就是去設置下繪製的範圍。
MainActivity
package com.example.lsn5;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = findViewById(R.id.image);
RevealDrawable drawable = new RevealDrawable(getResources().getDrawable(R.drawable.avft),getResources().getDrawable(R.drawable.avft_active));
imageView.setImageDrawable(drawable);
}
}
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/image"
android:background="@color/colorPrimaryDark"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
我們看下運行效果
看下原來的兩張圖片,我們分別截取左右兩部分
滑動變色
我們在上面的基礎上繼續完善我們的代碼
package com.dn_alan.myapplication;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class GallaryHorizonalScrollView extends HorizontalScrollView{
private LinearLayout container;
private int w;//單張圖的寬度
public GallaryHorizonalScrollView(Context context) {
super(context);
init();
}
public GallaryHorizonalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GallaryHorizonalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
container = new LinearLayout(getContext());
container.setLayoutParams(lp);
addView(container);
}
/**
* 漸變圖片
*/
private void reveal() {
//得到hzw滑出去的橫向距離,根據滾動的距離計算出當前滾動到了哪一張圖片
int scrollX = getScrollX();
//找到兩張漸變圖片的下標 ----左,右
//處在中間位置的兩張圖片
int indexLeft = scrollX / w;
int indexRight = indexLeft + 1;
//偏移了多少
float trans = scrollX % w;
int levelLeft = (int) (5000 - Math.abs(trans / w * 5000));
int levelRight = levelLeft + 5000;
Log.e("---------------","left = " + levelLeft + " right = " + levelRight);
int count = container.getChildCount();
for (int i = 0; i < count; i++) {
ImageView iv = (ImageView) container.getChildAt(i);
if(i == indexLeft){
iv.setImageLevel(levelLeft);
}else if(i == indexRight){
iv.setImageLevel(levelRight);
}else{
iv.setImageLevel(0);
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//獲取一個子View,因爲所有的子View的寬高都是一樣的。
View first = container.getChildAt(0);
//獲取子View 的寬,根據滾動距離,計算滑動到了哪個View
w = first.getWidth();
int padding = getWidth() / 2 - w / 2;
//給LinearLayout和hzw之間設置邊框距離。
container.setPadding(padding,0,padding,0);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
reveal();
}
public void addViews(){
container.removeAllViews();
for (int i = 0; i < mImgIds.length; i++) {
container.addView(getRevealView(i));
}
ImageView childAt = (ImageView) container.getChildAt(0);
childAt.setImageLevel(5000);
}
private View getRevealView(int i) {
ImageView iv = new ImageView(getContext());
Drawable d1 = getResources().getDrawable(mImgIds[i]);
Drawable d2 = getResources().getDrawable(mImgIdsActive[i]);
RevealDrawable rd = new RevealDrawable(d1,d2, RevealDrawable.HORIZONTAL);
iv.setImageDrawable(rd);
return iv;
}
private int[] mImgIds = new int[]{ //7個
R.drawable.avft,
R.drawable.box_stack,
R.drawable.bubble_frame,
R.drawable.bubbles,
R.drawable.bullseye,
R.drawable.circle_filled,
R.drawable.circle_outline,
R.drawable.avft,
R.drawable.box_stack,
R.drawable.bubble_frame,
R.drawable.bubbles,
R.drawable.bullseye,
R.drawable.circle_filled,
R.drawable.circle_outline
};
private int[] mImgIdsActive = new int[]{
R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,
R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,
R.drawable.circle_outline_active,
R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,
R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,
R.drawable.circle_outline_active
};
}
package com.dn_alan.myapplication;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class RevealDrawable extends Drawable {
private Drawable d1,d2;
private Rect mRect = new Rect();
private int mOrientation;
public static final int HORIZONTAL = 1;
public static final int VERTICAL = 2;
public RevealDrawable(Drawable d1, Drawable d2, int orientation){
this.d1 = d1;
this.d2 = d2;
this.mOrientation = orientation;
}
@Override
public int getIntrinsicWidth() {
return Math.max(d1.getIntrinsicWidth(),d2.getIntrinsicWidth());
}
@Override
public int getIntrinsicHeight() {
return Math.max(d1.getIntrinsicHeight(),d2.getIntrinsicHeight());
}
/**
* 制提供了畫布上下文,那麼就還需要提供一個可繪製的區域,下面方法就是用來指定繪製的區域。
* Drawable在繪製調用draw函數之前必須要先指定繪製的區域,這個區域也是Canvas中要繪製的區域。
* 一旦用戶改變了繪製區域時會激發onBoundsChange方法,派生類可以重載onBoundsChange來實現區域變更的處理。
*/
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
d1.setBounds(bounds);
d2.setBounds(bounds);
}
//當調用ImageView的setImageLevel()方法時候,會執行這個方法
//層級發生改變的話可以在這個方法裏做些處理,level的值爲0-10000
/**
* public final boolean setLevel(int level)
* public final int getLevel()
* 你可以用這兩個的方法來設置顯示的級別,以便進行一些繪製時的區間和條件控制,這個屬性並不是所有Drawable派生類都能用到,
* 如果設置有變化則會調用onLevelChange,派生類可以重載onLevelChange來實現級別變化的更新處理:
*/
@Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return super.onLevelChange(level);
}
@Override
public void draw(@NonNull Canvas canvas) {
int level = getLevel();
int gravity = level < 5000 ? Gravity.LEFT : Gravity.RIGHT;
float ratio = Math.abs(level / 5000f - 1);
//得到當前自身Drawable的矩形區域,其長和寬就是getIntrinsicWidth和getIntrinsicHeight方法返回的值
Rect bounds = getBounds();
int w = bounds.width();
int h = bounds.height();
if(mOrientation == HORIZONTAL){
w *= ratio;
}else if(mOrientation == VERTICAL){
h *= ratio;
}
Gravity.apply(
//從左邊扣還是從右邊扣
gravity,
//目標矩形寬
w,
//目標矩形高
h,
//被扣對象
bounds,
//承載對象
mRect
);
canvas.save();
//裁剪;裁剪了我們想要的繪製區域,矩形來確認需要剪裁的位置
canvas.clipRect(mRect);
//把圖形畫到傳入的 Canvas 上
d1.draw(canvas);
canvas.restore();
w = bounds.width();
h = bounds.height();
gravity = level > 5000 ? Gravity.LEFT : Gravity.RIGHT;
if(mOrientation == HORIZONTAL){
w -= w * ratio;
}else if(mOrientation == VERTICAL){
h -= h * ratio;
}
Gravity.apply(
//從左邊扣還是從右邊扣
gravity,
//目標矩形寬
w,
//目標矩形高
h,
//被扣對象
bounds,
//承載對象
mRect
);
canvas.save();
canvas.clipRect(mRect);
d2.draw(canvas);
canvas.restore();
}
@Override
public void setAlpha(int i) {
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}
package com.dn_alan.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
private ImageView iv;
private int[] mImgIds = new int[]{ //7個
R.drawable.avft,
R.drawable.box_stack,
R.drawable.bubble_frame,
R.drawable.bubbles,
R.drawable.bullseye,
R.drawable.circle_filled,
R.drawable.circle_outline,
R.drawable.avft,
R.drawable.box_stack,
R.drawable.bubble_frame,
R.drawable.bubbles,
R.drawable.bullseye,
R.drawable.circle_filled,
R.drawable.circle_outline
};
private int[] mImgIds_active = new int[]{
R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,
R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,
R.drawable.circle_outline_active,
R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active,
R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active,
R.drawable.circle_outline_active
};
public Drawable[] revealDrawables;
protected int level = 5000;
private GallaryHorizonalScrollView hzv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
hzv = (GallaryHorizonalScrollView) findViewById(R.id.hsv);
}
private void initData() {
revealDrawables = new Drawable[mImgIds.length];
for (int i = 0; i < mImgIds.length; i++) {
RevealDrawable rd = new RevealDrawable(
getResources().getDrawable(mImgIds[i]),
getResources().getDrawable(mImgIds_active[i]),
RevealDrawable.HORIZONTAL);
revealDrawables[i] = rd;
}
hzv.addViews();
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<com.dn_alan.myapplication.GallaryHorizonalScrollView
android:id="@+id/hsv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="#AA444444"
android:scrollbars="none"
/>
</LinearLayout>
我們看下效果
我們看下圖片資源,兩套圖片