溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android自定義View的三種實(shí)現(xiàn)方式總結(jié)

發(fā)布時間:2020-09-19 14:08:05 來源:腳本之家 閱讀:166 作者:賈永基 欄目:移動開發(fā)

在畢設(shè)項(xiàng)目中多處用到自定義控件,一直打算總結(jié)一下自定義控件的實(shí)現(xiàn)方式,今天就來總結(jié)一下吧。在此之前學(xué)習(xí)了郭霖大神博客上面關(guān)于自定義View的幾篇博文,感覺受益良多,本文中就參考了其中的一些內(nèi)容。

總結(jié)來說,自定義控件的實(shí)現(xiàn)有三種方式,分別是:組合控件、自繪控件和繼承控件。下面將分別對這三種方式進(jìn)行介紹。

(一)組合控件

組合控件,顧名思義就是將一些小的控件組合起來形成一個新的控件,這些小的控件多是系統(tǒng)自帶的控件。比如很多應(yīng)用中普遍使用的標(biāo)題欄控件,其實(shí)用的就是組合控件,那么下面將通過實(shí)現(xiàn)一個簡單的標(biāo)題欄自定義控件來說說組合控件的用法。

1、新建一個Android項(xiàng)目,創(chuàng)建自定義標(biāo)題欄的布局文件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="這是標(biāo)題"
    android:textColor="#ffffff"
    android:textSize="20sp" />

</RelativeLayout>

可見這個標(biāo)題欄控件還是比較簡單的,其中在左邊有一個返回按鈕,背景是一張事先準(zhǔn)備好的圖片back1_64.png,標(biāo)題欄中間是標(biāo)題文字。

2、創(chuàng)建一個類TitleView,繼承自RelativeLayout:

public class TitleView extends RelativeLayout {

  // 返回按鈕控件
  private Button mLeftBtn;
  // 標(biāo)題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);

  }

  // 為左側(cè)返回按鈕添加自定義點(diǎn)擊事件
  public void setLeftButtonListener(OnClickListener listener) {
    mLeftBtn.setOnClickListener(listener);
  }

  // 設(shè)置標(biāo)題的方法
  public void setTitleText(String title) {
    mTitleTv.setText(title);
  }
}

在TitleView中主要是為自定義的標(biāo)題欄加載了布局,為返回按鈕添加事件監(jiān)聽方法,并提供了設(shè)置標(biāo)題文本的方法。

3、在activity_main.xml中引入自定義的標(biāo)題欄:

<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>

4、在MainActivity中獲取自定義的標(biāo)題欄,并且為返回按鈕添加自定義點(diǎn)擊事件:

private TitleView mTitleBar;
     mTitleBar = (TitleView) findViewById(R.id.title_bar);

    mTitleBar.setLeftButtonListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        Toast.makeText(MainActivity.this, "點(diǎn)擊了返回按鈕", Toast.LENGTH_SHORT)
            .show();
        finish();
      }
    });

5、運(yùn)行效果如下:

Android自定義View的三種實(shí)現(xiàn)方式總結(jié)   

這樣就用組合的方式實(shí)現(xiàn)了自定義標(biāo)題欄,其實(shí)經(jīng)過更多的組合還可以創(chuàng)建出功能更為復(fù)雜的自定義控件,比如自定義搜索欄等。

 (二)自繪控件

自繪控件的內(nèi)容都是自己繪制出來的,在View的onDraw方法中完成繪制。下面就實(shí)現(xiàn)一個簡單的計(jì)數(shù)器,每點(diǎn)擊它一次,計(jì)數(shù)值就加1并顯示出來。

1、創(chuàng)建CounterView類,繼承自View,實(shí)現(xiàn)OnClickListener接口:

public class CounterView extends View implements OnClickListener {

  // 定義畫筆
  private Paint mPaint;
  // 用于獲取文字的寬和高
  private Rect mBounds;
  // 計(jì)數(shù)值,每點(diǎn)擊一次本控件,其值增加1
  private int mCount;

  public CounterView(Context context, AttributeSet attrs) {
    super(context, attrs);

    // 初始化畫筆、Rect
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBounds = new Rect();
    // 本控件的點(diǎn)擊事件
    setOnClickListener(this);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    mPaint.setColor(Color.BLUE);
    // 繪制一個填充色為藍(lán)色的矩形
    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、運(yùn)行效果如下:

Android自定義View的三種實(shí)現(xiàn)方式總結(jié)

(三)繼承控件

就是繼承已有的控件,創(chuàng)建新控件,保留繼承的父控件的特性,并且還可以引入新特性。下面就以支持橫向滑動刪除列表項(xiàng)的自定義ListView的實(shí)現(xiàn)來介紹。

1、創(chuàng)建刪除按鈕布局delete_btn.xml,這個布局是在橫向滑動列表項(xiàng)后顯示的:

<?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、創(chuàng)建CustomListView類,繼承自ListView,并實(shí)現(xiàn)了OnTouchListener和OnGestureListener接口:

public class CustomListView extends ListView implements OnTouchListener,
    OnGestureListener {

  // 手勢動作探測器
  private GestureDetector mGestureDetector;

  // 刪除事件監(jiān)聽器
  public interface OnDeleteListener {
    void onDelete(int index);
  }

  private OnDeleteListener mOnDeleteListener;

  // 刪除按鈕
  private View mDeleteBtn;

  // 列表項(xiàng)布局
  private ViewGroup mItemLayout;

  // 選擇的列表項(xiàng)
  private int mSelectedItem;

  // 當(dāng)前刪除按鈕是否顯示出來了
  private boolean isDeleteShown;

  public CustomListView(Context context, AttributeSet attrs) {
    super(context, attrs);

    // 創(chuàng)建手勢監(jiān)聽器對象
    mGestureDetector = new GestureDetector(getContext(), this);

    // 監(jiān)聽onTouch事件
    setOnTouchListener(this);
  }

  // 設(shè)置刪除監(jiān)聽事件
  public void setOnDeleteListener(OnDeleteListener listener) {
    mOnDeleteListener = listener;
  }

  // 觸摸監(jiān)聽事件
  @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 e2, float velocityX,
      float velocityY) {
    // 如果當(dāng)前刪除按鈕沒有顯示出來,并且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 e2, float distanceX,
      float distanceY) {
    return false;
  }

  @Override
  public void onLongPress(MotionEvent e) {

  }

}

3、定義列表項(xiàng)布局custom_listview_item.xml,它的結(jié)構(gòu)很簡單,只包含了一個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中對列表做初始化、設(shè)置列表項(xiàng)刪除按鈕點(diǎn)擊事件等處理:

public class MainActivity extends Activity {

  // 自定義Lv
  private CustomListView mCustomLv;
  // 自定義適配器
  private CustomListViewAdapter mAdapter;
  // 內(nèi)容列表
  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);
  }

  // 初始化內(nèi)容列表
  private void initContentList() {
    for (int i = 0; i < 20; i++) {
      contentList.add("內(nèi)容項(xiàng)" + i);
    }
  }

  @Override
  public void onBackPressed() {
    if (mCustomLv.isDeleteShown()) {
      mCustomLv.hideDelete();
      return;
    }
    super.onBackPressed();
  }

}

7、運(yùn)行效果如下:

Android自定義View的三種實(shí)現(xiàn)方式總結(jié)

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI