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

Summary of Three Implementation Methods of Android Custom View

The graduation project uses custom controls in many places, and I have always intended to summarize the implementation methods of custom controls. Today, I will summarize them. Before that, I learned several articles about custom View from the blog of God Guolin, and I found it very beneficial. This article refers to some of the content in it.

In summary, there are three ways to implement custom controls, namely: composite controls, hand-drawn controls, and inherited controls. Below, we will introduce these three methods respectively.

(I) Composite Controls

Composite controls, as the name implies, are a combination of some small controls to form a new control. These small controls are mostly system-provided controls. For example, the title bar control commonly used in many applications is actually a composite control. Therefore, the following will explain the usage of composite controls by implementing a simple custom title bar control.

1Create a new Android project and create a custom title bar layout file 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="This is the title"
    android:textColor="#ffffff"
    android:textSize="20sp" />
</RelativeLayout>

As can be seen, this title bar control is relatively simple, with a return button on the left and a background image back prepared in advance.1_64.png, the title text is in the middle of the title bar.

2Create a class TitleView, inheriting from RelativeLayout:

public class TitleView extends RelativeLayout {
  // Return button control
  private Button mLeftBtn;
  // Title Tv
  private TextView mTitleTv;
  public TitleView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // Load layout
    LayoutInflater.from(context).inflate(R.layout.title_bar, this);
    // Obtain controls
    mLeftBtn = (Button) findViewById(R.id.left_btn);
    mTitleTv = (TextView) findViewById(R.id.title_tv);
  }
  // Add a custom click event to the left back button
  public void setLeftButtonListener(OnClickListener listener) {
    mLeftBtn.setOnClickListener(listener);
  }
  // Method to set the title
  public void setTitleText(String title) {
    mTitleTv.setText(title);
  }
}

In TitleView, the main task is to load the layout for the custom title bar, add event listener methods for the back button, and provide methods to set the title text.

3In activity_main.xml, import the custom title bar:

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

4In MainActivity, obtain the custom title bar and add a custom click event to the back button:

private TitleView mTitleBar;
     mTitleBar = (TitleView) findViewById(R.id.title_bar);
    mTitleBar.setLeftButtonListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        Toast.makeText(MainActivity.this, "Clicked the back button", Toast.LENGTH_SHORT)
            .show();
        finish();
      }
    });

5The running effect is as follows:

   

In this way, a custom title bar is implemented by combining, and more combinations can create more complex custom controls, such as custom search bars, etc.

 (ii) Self-drawn control

All the content of the self-drawn control is drawn by itself, completed in the View's onDraw method. Below is an implementation of a simple counter, the count value increases each time it is clicked.1and display it.

1Create the CounterView class, inheriting from View and implementing the OnClickListener interface:

public class CounterView extends View implements OnClickListener {
  // Define the paint
  private Paint mPaint;
  // Used to get the width and height of the text
  private Rect mBounds;
  // Count value, the value increases each time this control is clicked1
  private int mCount;
  public CounterView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // Initialize the paint and Rect
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBounds = new Rect();
    // The click event of this control
    setOnClickListener(this);
  }
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(Color.BLUE);
    // Draw a rectangle with blue fill color
    canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
    mPaint.setColor(Color.YELLOW);
    mPaint.setTextSize(50);
    String text = String.valueOf(mCount);
    // Get the width and height of the text
    mPaint.getTextBounds(text, 0, text.length(), mBounds);
    float textWidth = mBounds.width();
    float textHeight = mBounds.height();
    // Draw string
    canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2
        + textHeight / 2, mPaint);
  }
  @Override
  public void onClick(View v) {
    mCount ++;
    // Redraw
    invalidate();
  }
}

2In activity_main.xml, introduce the custom layout:

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

3The running effect is as follows:

(3) Inheriting controls

It is to inherit existing controls, create new controls, retain the characteristics of the inherited parent controls, and can also introduce new features. Below, we will introduce the implementation of a custom ListView that supports horizontal swiping to delete list items.

1Create the delete button layout delete_btn.xml, which is displayed after horizontal swiping a list item:

<?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="Delete"
  android:textColor="#FFFFFF"
  android:textSize="16sp" >
</Button>

2Create the CustomListView class, inheriting from ListView, and implementing the OnTouchListener and OnGestureListener interfaces:

public class CustomListView extends ListView implements OnTouchListener,
    OnGestureListener {
  // Gesture detection action detector
  private GestureDetector mGestureDetector;
  // Delete event listener
  public interface OnDeleteListener {
    void onDelete(int index);
  }
  private OnDeleteListener mOnDeleteListener;
  // Delete button
  private View mDeleteBtn;
  // List item layout
  private ViewGroup mItemLayout;
  // The selected list item
  private int mSelectedItem;
  // Whether the current delete button is displayed
  private boolean isDeleteShown;
  public CustomListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // Create a gesture listener object
    mGestureDetector = new GestureDetector(getContext(), this);
    // Listen to onTouch event
    setOnTouchListener(this);
  }
  // Set delete listening event
  public void setOnDeleteListener(OnDeleteListener listener) {
    mOnDeleteListener = listener;
  }
  // Touch listening event
  @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) {
    // If the current delete button is not displayed and the sliding speed in the x direction is greater than the sliding speed in the y direction
    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;
  }
  // Hide the delete button
  public void hideDelete() {
    mItemLayout.removeView(mDeleteBtn);
    mDeleteBtn = null;
    isDeleteShown = false;
  }
  public boolean isDeleteShown() {
    return isDeleteShown;
  }
  /**
   * The following methods are not used in this example
   */
  @Override
  public void onShowPress(MotionEvent e) {
  }
  @Override
  public boolean onSingleTapUp(MotionEvent e) {
    return false;
  }
  @Override
  public boolean onScroll(MotionEvent e)1, MotionEvent e}}2, float distanceX,
      float distanceY) {
    return false;
  }
  @Override
  public void onLongPress(MotionEvent e) {
  }
}

3Define the list item layout custom_listview_item.xml, which has a simple structure, containing only a 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>

4Define the adapter class CustomListViewAdapter, inheriting from 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;
  }
}

5In activity_main.xml, import the custom 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>

6In MainActivity, initialize the list, set the click event for the delete button of the list item, and other processing:

public class MainActivity extends Activity {
  // Custom Lv
  private CustomListView mCustomLv;
  // Custom Adapter
  private CustomListViewAdapter mAdapter;
  // Content List
  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);
  }
  // Initialize content list
  private void initContentList() {
    for (int i = 0; i < 20; i++) {
      contentList.add("Content Item" + i);
    }
  }
  @Override
  public void onBackPressed() {
    if (mCustomLv.isDeleteShown()) {
      mCustomLv.hideDelete();
      return;
    }
    super.onBackPressed();
  }
}

7The running effect is as follows:

That's all for this article, I hope it will be helpful to everyone's learning, and I also hope everyone will support the Yelling Tutorial more.

Statement: The content of this article is from the Internet, the copyright belongs to the original author, the content is contributed and uploaded by Internet users spontaneously, this website does not own the copyright, has not been edited by humans, and does not assume relevant legal liabilities. If you find any content suspected of copyright infringement, please send an email to: notice#w3Please report via email to codebox.com (replace # with @ when sending email) and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.

You May Also Like