English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Androidのカスタムビューの3つの実現方法をまとめ

卒業設計プロジェクトではカスタムコンポーネントが多处に使用されています。常にカスタムコンポーネントの実装方法をまとめようと考えていました。今日はそのまとめをしようと思います。その前に、郭霖氏のブログでいくつかのカスタムViewに関する記事を読みました。とても有益で、この記事ではその中の一部を参照しています。

要約すると、カスタムコンポーネントの実装には3つの方法があります。それは、組み合わせコンポーネント、自作コンポーネント、継承コンポーネントです。以下にそれぞれの方法について紹介します。

(一)コンポーネントを組み合わせる

コンポーネントを組み合わせることは、その名の通り、いくつかの小さなコンポーネントを組み合わせて新しいコンポーネントを作成することです。これらの小さなコンポーネントはほとんどがシステムの標準コンポーネントです。例えば、多くのアプリケーションで一般的に使用されるタイトルバーのコンポーネントは、実際にはコンポーネントを組み合わせたものです。それでは、シンプルなタイトルバーのカスタムコンポーネントを実装することで、コンポーネントを組み合わせる方法について説明します。

1、新しいAndroidプロジェクトを作成し、カスタムタイトルバーのレイアウトファイルtitle_bar.xmlを作成します:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:background="#0000ff" >
  <Button
    android:id="@"+id/left_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_margin="5dp"
    android:background="@drawable/back1_64" />
  <TextView
    android:id="@"+id/title_tv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="これはタイトル"
    android:textColor="#ffffff"
    android:textSize="20sp" />
</RelativeLayout>

このタイトルバーのコントロールは比較的シンプルで、左側に戻るボタンがあり、背景は事前に準備された画像backです1_64.png、タイトルバーの中央にはタイトルテキストがあります。

2、クラスTitleViewを作成し、RelativeLayoutを継承します:

public class TitleView extends RelativeLayout {
  // 戻るボタンのコントロール
  private Button mLeftBtn;
  // タイトルTv
  private TextView mTitleTv;
  public TitleView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // レイアウトをロード
    LayoutInflater.from(context).inflate(R.layout.title_bar, this);
    // コントロールを取得
    mLeftBtn = (Button) findViewById(R.id.left_btn);
    mTitleTv = (TextView) findViewById(R.id.title_tv);
  }
  // 左側の戻るボタンのカスタムクリックイベントを追加します
  public void setLeftButtonListener(OnClickListener listener) {
    mLeftBtn.setOnClickListener(listener);
  }
  // タイトルを設定するメソッド
  public void setTitleText(String title) {
    mTitleTv.setText(title);
  }
}

TitleViewでは、カスタムのタイトルバーにレイアウトをロードし、戻るボタンのイベントリスナーを追加し、タイトルテキストを設定するメソッドを提供しています。

3activity_main.xmlにカスタムのタイトルバーをインクルードします:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
  android:id="@"+id/main_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  <com.example.test.TitleView
    android:id="@"+id/title_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >
  </com.example.test.TitleView>
</LinearLayout>

4MainActivityでカスタムのタイトルバーを取得し、戻るボタンのカスタムクリックイベントを追加します:

private TitleView mTitleBar;
     mTitleBar = (TitleView) findViewById(R.id.title_bar);
    mTitleBar.setLeftButtonListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        Toast.makeText(MainActivity.this, "戻るボタンをクリックしました", Toast.LENGTH_SHORT)
            .show();
        finish();
      }
    });

5、実行結果は以下の通りです:

   

これで、カスタムタイトルバーを組み合わせて実現しました。さらに組み合わせることで、より複雑なカスタムコントロール、例えばカスタム検索バーなどを作成できます。

 (二)自描画コントロール

自描画コントロールの内容はすべて自分で描画しており、ViewのonDrawメソッドで描画が完了します。以下に簡単なカウンタを実装し、クリックするとカウント値が増加します。1それを表示します。

1、CounterViewクラスをViewから継承し、OnClickListenerインターフェースを実装:

public class CounterView extends View implements OnClickListener {
  // 画刷の定義
  private Paint mPaint;
  // テキストの幅と高さを取得するために使用
  private Rect mBounds;
  // カウント値、このコントロールをクリックするたびにその値が増加1
  private int mCount;
  public CounterView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // 画刷、Rectの初期化
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBounds = new Rect();
    // このコントロールのクリックイベント
    setOnClickListener(this);
  }
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(Color.BLUE);
    // 青色塗りつぶしの矩形を描画
    canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
    mPaint.setColor(Color.YELLOW);
    mPaint.setTextSize(50);
    String text = String.valueOf(mCount);
    // 文字の幅と高さを取得
    mPaint.getTextBounds(text, 0, text.length(), mBounds);
    float textWidth = mBounds.width();
    float textHeight = mBounds.height();
    // 文字を描画
    canvas.drawText(text, getWidth(), / 2 - textWidth / 2, getHeight() / 2
        + textHeight / 2, mPaint);
  }
  @Override
  public void onClick(View v) {
    mCount ++;
    // 再描画
    invalidate();
  }
}

2、activity_main.xmlにこのカスタムレイアウトをインクルードします:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
  android:id="@"+id/main_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  <com.example.test.CounterView
    android:id="@"+id/counter_view"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_gravity="center_horizontal|top"
    android:layout_margin="20dp" />
</LinearLayout>

3、実行結果は以下の通りです:

(三)コントロールの継承

すでにあるコントロールを継承し、新しいコントロールを作成し、継承した親コントロールの特性を保持しつつ、新しい特性を導入することもできます。以下では、リストアイテムを横にスライドして削除するカスタムListViewの実装について説明します。

1、削除ボタンのレイアウトdelete_btn.xmlを作成します。このレイアウトはリストアイテムを横にスライドした後に表示されます:

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="#FF0000"
  android:padding="5dp"
  android:text="削除"
  android:textColor="#FFFFFF"
  android:textSize="16sp" >
</Button>

2、CustomListViewクラスを生成し、ListViewを継承し、OnTouchListenerおよびOnGestureListenerインターフェースを実装します:

public class CustomListView extends ListView implements OnTouchListener,
    OnGestureListener {
  // ジェスチャー動作検出器
  private GestureDetector mGestureDetector;
  // 削除イベントリスナー
  public interface OnDeleteListener {
    void onDelete(int index);
  }
  private OnDeleteListener mOnDeleteListener;
  // 削除ボタン
  private View mDeleteBtn;
  // リストアイテムレイアウト
  private ViewGroup mItemLayout;
  // 選択されたリストアイテム
  private int mSelectedItem;
  // 現在の削除ボタンが表示されているかどうか
  private boolean isDeleteShown;
  public CustomListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // ジェスチャーリスナーオブジェクトを生成
    mGestureDetector = new GestureDetector(getContext(), this);
    // onTouchイベントをリスナーに設定
    setOnTouchListener(this);
  }
  // 削除リスナーイベントを設定
  public void setOnDeleteListener(OnDeleteListener listener) {
    mOnDeleteListener = listener;
  }
  // タッチリスナーイベント
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    if (isDeleteShown) {
      hideDelete();
      return false;
    } else {
      return mGestureDetector.onTouchEvent(event);
    }
  }
  @Override
  public boolean onDown(MotionEvent e) {
    if (!isDeleteShown) {
      mSelectedItem = pointToPosition((int) e.getX(), (int) e.getY());
    }
    return false;
  }
  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e}}2, float velocityX,
      float velocityY) {
    // 現在の削除ボタンが表示されていない場合で、x方向のスライド速度がy方向のスライド速度よりも大きい
    if (!isDeleteShown && Math.abs(velocityX) > Math.abs(velocityY)) {
      mDeleteBtn = LayoutInflater.from(getContext()).inflate(
          R.layout.delete_btn, null);
      mDeleteBtn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          mItemLayout.removeView(mDeleteBtn);
          mDeleteBtn = null;
          isDeleteShown = false;
          mOnDeleteListener.onDelete(mSelectedItem);
        }
      });
      mItemLayout = (ViewGroup) getChildAt(mSelectedItem
          - getFirstVisiblePosition());
      RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
          LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
      params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
      params.addRule(RelativeLayout.CENTER_VERTICAL);
      mItemLayout.addView(mDeleteBtn, params);
      isDeleteShown = true;
    }
    return false;
  }
  // 削除ボタンを非表示にします
  public void hideDelete() {
    mItemLayout.removeView(mDeleteBtn);
    mDeleteBtn = null;
    isDeleteShown = false;
  }
  public boolean isDeleteShown() {
    return isDeleteShown;
  }
  /**
   * 後方のいくつかのメソッドはこのサンプルでは使用されていません
   */
  @Override
  public void onShowPress(MotionEvent e) {
  }
  @Override
  public boolean onSingleTapUp(MotionEvent e) {
    return false;
  }
  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e}}2, float distanceX,
      float distanceY) {
    return false;
  }
  @Override
  public void onLongPress(MotionEvent e) {
  }
}

3、定義リストアイテムレイアウトcustom_listview_item.xml、その構造はシンプルで、TextViewのみを含んでいます:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:descendantFocusability="blocksDescendants" >
  <TextView
    android:id="@"+id/content_tv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_margin="30dp"
    android:gravity="center_vertical|left" />
</RelativeLayout>

4、定義アダプタクラスCustomListViewAdapter、ArrayAdapter<String>を継承:

public class CustomListViewAdapter extends ArrayAdapter<String> {
  public CustomListViewAdapter(Context context, int textViewResourceId,
      List<String> objects) {
    super(context, textViewResourceId, objects);
  }
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    View view;
    if (convertView == null) {
      view = LayoutInflater.from(getContext()).inflate(
          R.layout.custom_listview_item, null);
    } else {
      view = convertView;
    }
    TextView contentTv = (TextView) view.findViewById(R.id.content_tv);
    contentTv.setText(getItem(position));
    return view;
  }
}

5、activity_main.xmlにカスタムのListViewをインクルードします:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
  android:id="@"+id/main_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  <com.example.test.CustomListView
    android:id="@"+id/custom_lv"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
</LinearLayout>

6、MainActivityでリストの初期化、リストアイテムの削除ボタンのクリックイベントなどの処理を行います:

public class MainActivity extends Activity {
  // カスタムLv
  private CustomListView mCustomLv;
  // カスタムアダプター
  private CustomListViewAdapter mAdapter;
  // 内容列表
  private List<String> contentList = new ArrayList<String>();
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
    initContentList();
    mCustomLv = (CustomListView) findViewById(R.id.custom_lv);
    mCustomLv.setOnDeleteListener(new OnDeleteListener() {
      @Override
      public void onDelete(int index) {
        contentList.remove(index);
        mAdapter.notifyDataSetChanged();
      }
    });
    mAdapter = new CustomListViewAdapter(this, 0, contentList);
    mCustomLv.setAdapter(mAdapter);
  }
  // 内容リストの初期化
  private void initContentList() {
    for (int i = 0; i < 20; i++) {
      contentList.add("内容項目" + i);
    }
  }
  @Override
  public void onBackPressed() {
    if (mCustomLv.isDeleteShown()) {
      mCustomLv.hideDelete();
      return;
    }
    super.onBackPressed();
  }
}

7、実行結果は以下の通りです:

これで本文の全てです。皆様の学習に役立つことを願っています。また、呐喊教程を多くの皆様にサポートしていただけますようにお願いいたします。

声明:本文の内容はインターネットから収集され、著作権者に帰属します。インターネットユーザーにより自発的に貢献し、アップロードされた内容です。本サイトは所有権を持ちません。人工的な編集は行われていません。また、関連する法的責任を負いません。著作権侵害を疑う内容が見つかりましたら、メールまでお知らせください:notice#oldtoolbag.com(メールを送信する際、#を@に変更してください。侵害を報告する場合、関連する証拠を提供してください。一旦確認が取れましたら、本サイトは即座に侵害を疑われる内容を削除します。)

おすすめ