カスタムビューの基本的な流れ

特殊な効果や機能が必要な場合がありますが、システム コントロールではニーズを満たせないため、自分でコントロールを定義する必要があります。

カスタムビューのプロセス

ビューの継承

View をカスタマイズするには、まず View またはそのサブクラスを継承する必要があります。達成する必要がある効果がより複雑な場合は、通常、View を継承する必要があります。システム コントロールといくつかの特殊効果が必要な場合もあります。その場合は、View サブクラスを継承できます。 (TextViewなど)

自分でレイアウトをデザインしたり、他のコントロールを組み合わせたりする場合は、ViewGroup または LinearLayout、FrameLayout などのシステムに付属するレイアウトを継承する必要があります。

オーバーライドコンストラクター

カスタム ビューは少なくとも 2 つの構築メソッドをオーバーライドする必要があります

  • Java コードでコントロールを直接作成するために使用されるコンストラクター
public CustomView(Context context) {
        this(context, null);
}
  • Viewで使用するコンストラクターをxmlファイルで定義します。
public CustomView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
}

public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
}

カスタム XML の属性

まず、新しい res/values/custom_view_attrs.xml を作成し、次のように宣言する必要があります。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomView">
        <attr name="custom_width" format="dimension" />
    </declare-styleable>
</resources>

次に、それを XML レイアウト ファイルで宣言できます。

<?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"
    tools:context="cn.lkllkllkl.customview.MainActivity">

    <cn.lkllkllkl.customview.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:custom_width="100dp"/>

</LinearLayout>

次に、このプロパティの値を取り出して、カスタム ビューのコンストラクターで使用できます。

public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr){
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        float customWidth = 0;
        TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
        customWidth = typeArray.getDimension(R.styleable.CustomView_custom_width, 100);
        // 需要记得回收
        typeArray.recycle();
}

onMeasure方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);    

スペックの測定

ここで onMeasure の 2 つのパラメーターを理解するには、まず MeasureSpec クラスを理解する必要があります。

MeasureSpec は、SpecMode (モード) と SpecSize (特定のサイズ) を int (つまり、widthMeasureSpec、heightMeasureSpec) にパックします。最初の 2 桁は SpecMode を表し、最後の 30 桁はサイズを表します。

SpecModeには3種類あります

  • 未指定:親コンテナにはビューのサイズに制限がありません。通常、使用するカスタム ビューは少なくなります。

  • EXACTLY: xml ファイルの dp で特定のサイズを設定するか、match_parent に設定されている場合は SpecMode を EXACTLY に設定します。このモードは、SpecSize がカスタム ビューのサイズとして直接使用されることを意味します

  • AT_MOST: XML ファイルのwrap_contentに対応し、設定サイズがSpecSizeを超えることができないことを示します。

通常、wrap_content をサポートするビューが必要な場合にのみ onMeasure を書き換える必要があります。書き換えない場合、wrap_content の効果は match_parent と同じです。通常、次のコードで十分です

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        // dp转换成px, 事先定义的默认宽高单位为dp
        int defaultWidth = dp2px(getContext(), DEFAULT_WIDTH);
        int defaultHeight = dp2px(getContext(), DEFAULT_HEIGHT);

        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            widthSize = Math.min(defaultWidth, widthSize);
            heightSize = Math.min(defaultHeight, heightSize);
        } else if (widthMode == MeasureSpec.AT_MOST) {
            widthSize = Math.min(defaultWidth, widthSize);
        } else if (heightMode == MeasureSpec.AT_MOST){
            heightSize = Math.min(defaultHeight, heightSize);
        }
        
        setMeasuredDimension(widthSize, heightSize);
    }

一般に、さまざまな画面に適応するには、dp を単位として使用する必要があり、setMeasuredDimension は px を単位として使用するため、上記のコードは設定したデフォルトの幅と高さを px に変換します。変換方法は次のとおりです。

public static int dp2px(Context context, float dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dp, context.getResources().getDisplayMetrics());
    }

onLayout メソッド

一般に、カスタム ViewGroup は、サブビューを放出するためにこのメソッドを書き直す必要があります。

onDraw メソッド

onDraw メソッドを使用すると、ビューを画面に描画できます。ビューでパディング属性をサポートしたい場合は、onDraw で行う必要があります。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /* mPaint为成员变量,画笔Paint的对象,
         * 在构造函数中进行初始化,可以设置一些在canvas上绘制的通用属性
         */
        mPaint.setColor(Color.RED);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        //在View所占区域绘制一条对角线
        canvas.drawLine(paddingLeft, paddingTop,
                getWidth() - paddingRight, getHeight() - paddingBottom, mPaint);
    }

予防

  • onMeasure、onLayout、onDraw の 3 つのメソッド、特に onDraw メソッドがこれを呼び出す可能性があるため、頻繁なメモリ割り当てによるメモリ ジッターを避けるため、またはフレームを引き起こす時間のかかる操作を実行するために、これらのメソッドでオブジェクトを作成しないことが最善です。スキップ

  • View には post series メソッドがあるため、View でハンドラーを使用する必要はありません

  • View に時間内に停止する必要がある作成スレッドまたはアニメーションがある場合は、View#onDetachedFromWindow が適しています。

  • ビューにスライディングのネストがある場合、スライディングの競合を適切に処理する必要があります。

参照

 

おすすめ

転載: blog.csdn.net/u013773608/article/details/130337550