Android でのカスタム ViewGroup の使用の概要

高洛峰
リリース: 2017-01-16 16:24:21
オリジナル
1494 人が閲覧しました

分類

カスタム レイアウトは 2 つの状況に分類できます。

ViewGroup をカスタマイズし、LinearLayout、RelativeLayout などとは異なるいくつかの ViewGroup を作成します。例: API 14 以降に追加された GridLayout、デザイン サポート ライブラリの CoordinatorLayout など。

既存のレイアウトをカスタマイズし、特別な機能を追加します。例: パーセントサポートライブラリの TableLayout と PercentFrameLayout など。

プロセス

カスタムビューのプロセスは、onMeasure()->onLayout()->onDraw()です。 ViewGroup をカスタマイズする場合、通常は onDraw を実装する必要はありません。もちろん、CoordinatorLayout などの特別な要件が必要になる場合があります。

つまり、onMeasure と onLayout は基本的に、私たちが接触するほとんどの ViewGroups を実行できます。ただし、onMeasure で ViewGroup のサイズを測定し、onLayout で子ビューの位置を計算する方法を知るだけでは十分ではありません。

例: ViewGroup の子ビューのプロパティを設定するにはどうすればよいですか?

一例です。

カスタム ViewGroup を作成し、ViewGroup に比例して子ビューのサイズ (長さと幅) を制御する属性を追加します。

LinearLayout であると仮定して、最初に CustomLinearLayout を定義します。

public class CustomLinearLayout extends LinearLayout {
  public CustomLinearLayout(Context context) {
    super(context);
  }
 
  public CustomLinearLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  }
 
  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }
 
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }
}
ログイン後にコピー

その他のことはさておき、子ビューのサイズを制御するために属性を追加する必要があります。次に、まず value/attr.xml でその属性を定義します (CustomLinearLayout_Layout を使用して CustomLinearLayout と区別します。もちろん名前は任意です)。

<declare-styleable name="CustomLinearLayout_Layout">
  <!-- 定义比例 -->
  <attr name="inner_percent" format="float"/>
</declare-styleable>
ログイン後にコピー

ViewGroup は、addView() を呼び出すときに最終的にこのメソッドを呼び出します。

public void addView(View child, int index, LayoutParams params)
ログイン後にコピー

この params は View の構成を表し、ViewGroup.LayoutParams には幅と高さが含まれ、LinearLayout.LayoutParams には重み属性などが追加されます。次に、LayoutParams を実装する必要があります。それでは今のところはここまでです。

public class CustomLinearLayout extends LinearLayout {
  public CustomLinearLayout(Context context) {
    super(context);
  }
 
  public CustomLinearLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  }
 
  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }
 
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }
  public static class LayoutParams extends LinearLayout.LayoutParams {
 
    private float innerPercent;
 
    private static final int DEFAULT_WIDTH = WRAP_CONTENT;
    private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
 
    public LayoutParams() {
      super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
      innerPercent = -1.0f;
    }
 
    public LayoutParams(float innerPercent) {
      super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
      this.innerPercent = innerPercent;
    }
 
    public LayoutParams(ViewGroup.LayoutParams p) {
      super(p);
    }
 
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public LayoutParams(LinearLayout.LayoutParams source) {
      super(source);
    }
 
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public LayoutParams(LayoutParams source) {
      super(source);
      this.innerPercent = source.innerPercent;
    }
 
    public LayoutParams(Context c, AttributeSet attrs) {
      super(c, attrs);
      init(c, attrs);
    }
 
    private void init(Context context, AttributeSet attrs) {
      TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLinearLayout_Layout);
      innerPercent = a.getFloat(R.styleable.CustomLinearLayout_Layout_inner_percent, -1.0f);
      a.recycle();
    }
  }
}
ログイン後にコピー

これで、XML で属性を使用できるようになりました。

<?xml version="1.0" encoding="utf-8"?>
<com.egos.samples.custom_layout.CustomLinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="200dp"
  android:layout_height="200dp"
  android:id="@+id/test_layout"
  android:background="#ffff0000"
  android:gravity="center"
  android:orientation="vertical">
  <ImageView
    android:text="Egos"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:onClick="add"
    android:background="#ff00ff00"
    app:inner_percent="0.8"/>
</com.egos.samples.custom_layout.CustomLinearLayout>
ログイン後にコピー

柔らかいだけで効果はありません。

それでは、Child View のサイズを制御するにはどうすればよいでしょうか?もちろんonMeasureで制御しています。 addView は次のコードを実行します。

requestLayout();
invalidate(true);
ログイン後にコピー

この場合、onMeasure() と onLayout() が再度実行されます。 onMeasure()メソッドを実装した後は、LinearLayoutを継承しているので、実際にはmeasureChildBeforeLayout()が処理されます。最後のステップは、ChildBeforeLayout を測定するときに子ビューのサイズを処理することです。

@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
                    int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
  // 在xml强制写成match_parent,然后在这里强制设置成
  if (child != null && child.getLayoutParams() instanceof LayoutParams &&
      ((LayoutParams) child.getLayoutParams()).innerPercent != -1.0f) {
    parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentWidthMeasureSpec) *
        ((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentWidthMeasureSpec));
    parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentHeightMeasureSpec) *
        ((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentHeightMeasureSpec));
    super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
      parentHeightMeasureSpec, heightUsed);
  } else {
    super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
        parentHeightMeasureSpec, heightUsed);
  }
}
ログイン後にコピー

このようにして、最初のニーズを達成できます。

Android でのカスタム ViewGroup の使用の概要

実際には、処理する必要がある詳細がまだいくつかあります。次のコードです。

/**
 * 当checkLayoutParams返回false的时候就会执行到这里的generateLayoutParams
 */
 @Override
protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
  return super.generateLayoutParams(lp);
}
 
/**
 * 当addView的时候没有设置LayoutParams的话就会默认执行这里的generateDefaultLayoutParams
 */
@Override
protected LayoutParams generateDefaultLayoutParams() {
  return new LayoutParams();
}
 
/**
 * 写在xml中属性的时候就会执行这里的generateLayoutParams
 */
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
  return new LayoutParams(getContext(), attrs);
}
ログイン後にコピー

まとめると

ViewとViewGroupをカスタマイズする際に注意が必要なのは、onMeasure、onLayout、onDrawです。

ViewGroup 自体のプロパティと子ビューのプロパティを区別します。

サポートパッケージ内のコードを参照でき、デバッグも非常に便利です。

Android 開発を行う場合、ビューをカスタマイズする必要がある場所は確かにたくさんありますが、そのほとんどには対応するオープンソース ライブラリがあります。ただし、ViewGroup をカスタマイズする方法を理解する必要があります。

Android でのカスタム ViewGroup の使用の概要に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート