生放送ソフトのソースコードAndroidカスタムViewでアニメーション効果を実現テーマ色を切り替える


このような効果を実現するには、まず、Xfermodeの画像混合モードにおけるPorterDuffについて知る.Mode.CLEARは、元の画像の一部の描画内容を消去するために使用することができ、消しゴムであり、画像上の任意の場所を消去することができると理解することができる.
次にcanvasにもレイヤーの概念があります.レイヤーは何ですか.簡単に言えば、1階1階の画像が同じ場所に重なっています.例えば、摩天楼があります.1階、2階、3階...18階などがあります.ビルの真上からビルを見下ろすと、下の何階も最上階に押されているので、最上階しか見えません.レイヤーの概念も同じで、私たちは正常に最上階のレイヤーしか見えず、他のレイヤーが覆われています.
だから、皆さんはすでにこのアニメーション効果の実現原理を推測したはずです.間違いありません.私たちは全部で2階が必要です.1階はテーマ変更前の効果図を残しています.2階はテーマ変更後の効果図です.それから、PorterDuffを使います.Mode.CLEARというモードは、drawCircleが円を描くことで古いスタイルを消します.古いスタイルが消去されると、下に押された新しいスタイル、つまりテーマが変更された後のスタイルが見えます.1、前のインタフェースのスタイルを変更し、bitmapで保存するとともに、古いレイヤーを消去するために必要な円の半径を得る必要があります.
// View          bg 
private void createbg(){
    rootView=(ViewGroup)((Activity)getContext()).getWindow().getDecorView();
    totalRadius=rootView.getMeasuredWidth()>rootView.getMeasuredHeight()?rootView.getMeasuredWidth():rootView.getMeasuredHeight();
    rootView.setDrawingCacheEnabled(true);
    bg=Bitmap.createBitmap(rootView.getDrawingCache(), 0, 0, rootView.getMeasuredWidth(), rootView.getMeasuredHeight());
    rootView.setDrawingCacheEnabled(false);
    attachToRootView();
}

rootView:元のインタフェースActivityのView
totalRadius:合計で描画する円の半径
bg:元のインタフェーススタイルを保存するBitmap
attachToRootView():このViewをrootViewに追加する方法(レイアウトコントロールを動的に追加)
2、saveLayerメソッドで新しいレイヤーを追加して古いスタイルを保存し、古いスタイルをアニメーション形式で消去する(追加後の操作はすべて新しいレイヤーで行われることに注意)
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //      
    if(!start)return;
    //                 ,     
    if(radius>totalRadius){
        animateFinish();
        return;
    }
    //         
    int layer = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
    canvas.drawBitmap(bg, 0, 0, null);
    canvas.drawCircle(0, 0, radius+perRadius, paint);
    radius+=perRadius;
    canvas.restoreToCount(layer);
    postInvalidateDelayed(5);
}
 
//    ,    
private void animateFinish(){
    start=false;
    bg.recycle();
    radius=0;
}

3、Activityでクリックボタンの傍受と新しいスタイルの更新を完了する
private void initView(){
    animatorThemeView=new MyAnimatorThemeView(this);
    textView=findViewById(R.id.tv_name);
    findViewById(R.id.btn_change).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(!animatorThemeView.hasStart()){
                clickNum++;
                //    
                animatorThemeView.start();
                //  textView   
                textView.setTextColor(getResources().getColor(tvColors[clickNum%2]));
                textView.setBackground(getDrawable(bgColors[clickNum%2]));
            }
        }
    });
}

以下に、完全なカスタムViewコードを示します.
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.view.View;
import android.view.ViewGroup;
 
public class MyAnimatorThemeView extends View {
    private Paint paint;
    private Bitmap bg;
    private ViewGroup rootView;
    private boolean start=false;
    private int radius=0,totalRadius=3000,perRadius=55;   //          ;          ;       
 
    public MyAnimatorThemeView(Context context){
        super(context);
        init();
    }
 
    private void init(){
        paint=new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //      
        if(!start)return;
        //                 ,     
        if(radius>totalRadius){
            animateFinish();
            return;
        }
        //         
        int layer = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
        canvas.drawBitmap(bg, 0, 0, null);
        canvas.drawCircle(0, 0, radius+perRadius, paint);
        radius+=perRadius;
        canvas.restoreToCount(layer);
        postInvalidateDelayed(5);
    }
 
    //    
    public void start(){
        if(!start){
            createbg();
            start=true;
            invalidate();
        }
    }
 
    // View          bg 
    private void createbg(){
        rootView=(ViewGroup)((Activity)getContext()).getWindow().getDecorView();
        totalRadius=rootView.getMeasuredWidth()>rootView.getMeasuredHeight()?rootView.getMeasuredWidth():rootView.getMeasuredHeight();
        rootView.setDrawingCacheEnabled(true);
        bg=Bitmap.createBitmap(rootView.getDrawingCache(), 0, 0, rootView.getMeasuredWidth(), rootView.getMeasuredHeight());
        rootView.setDrawingCacheEnabled(false);
        attachToRootView();
    }
 
    //  View      
    private void attachToRootView() {
        if(this.getParent()==null){
            this.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            rootView.addView(this);
        }
    }
 
    //    ,    
    private void animateFinish(){
        start=false;
        bg.recycle();
        radius=0;
    }
 
    //             
    public boolean hasStart(){
        return start;
    }
}

Activityクラスのコード:
public class MyAnimatorThemeAct extends AppCompatActivity {
    private MyAnimatorThemeView animatorThemeView;
    private TextView textView;
    private int clickNum=0;
    private int[] tvColors={R.color.white,R.color.black};
    private int[] bgColors={R.color.light_black,R.color.blue};
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_animator_theme);
        initView();
    }
 
    private void initView(){
        animatorThemeView=new MyAnimatorThemeView(this);
        textView=findViewById(R.id.tv_name);
        findViewById(R.id.btn_change).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!animatorThemeView.hasStart()){
                    clickNum++;
                    animatorThemeView.start();
                    textView.setTextColor(getResources().getColor(tvColors[clickNum%2]));
                    textView.setBackground(getDrawable(bgColors[clickNum%2]));
                }
            }
        });
    }
}

xmlレイアウトファイルは次のとおりです.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/relative"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/btn_change"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginRight="10dp"
        android:text="  "/>
 
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="350dp"
        android:layout_height="550dp"
        android:paddingHorizontal="10dp"
        android:text="@string/str1"
        android:layout_centerInParent="true"
        android:textSize="18sp"
        android:gravity="center_vertical"
        android:textColor="@color/white"
        android:background="@color/light_black"/>
</RelativeLayout>