English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Nel mio progetto di tesi sono stati utilizzati molti controlli personalizzati, e ho sempre avuto l'intenzione di fare una panoramica dell'implementazione dei controlli personalizzati. Oggi, mi sono deciso a fare questo. Prima di tutto, ho studiato alcuni articoli di blog del grande maestro Guo Lin, e ho trovato che mi hanno portato molto beneficio, quindi in questo articolo ho riferimento alcuni contenuti di quegli articoli.
In sintesi, l'implementazione di controlli personalizzati ha tre modi, ovvero: combinazione di controlli, disegno personalizzato e ereditarietà di controlli. Di seguito esploreremo questi tre modi rispettivamente.
(一)Combinazione di controlli
La combinazione di controlli, come si dice, è quella di combinare alcuni piccoli controlli per formare un nuovo controllo, questi piccoli controlli sono spesso controlli di sistema. Ad esempio, molti controlli di barra di titolo utilizzati nelle applicazioni sono controlli di combinazione, quindi di seguito esploreremo l'uso dei controlli di combinazione attraverso l'implementazione di un controllo di barra di titolo personalizzato semplice.
1、Crea un nuovo progetto Android, crea un file di layout personalizzato 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="Questo è il titolo" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout>
Vediamo che questa barra di titolo è piuttosto semplice, con un pulsante di ritorno sulla sinistra, lo sfondo è un'immagine preparata in anticipo back1_64.png e il testo del titolo al centro della barra di titolo.
2、Crea una classe TitleView, derivata da RelativeLayout:
public class TitleView extends RelativeLayout { // Controllo di pulsante di ritorno private Button mLeftBtn; // Controllo di testo del titolo private TextView mTitleTv; public TitleView(Context context, AttributeSet attrs) { super(context, attrs); // Carica la layout LayoutInflater.from(context).inflate(R.layout.title_bar, this); // Ottieni il controllo 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中主要是为自定义的标题栏加载了布局,为返回按钮添加事件监听方法,并提供了设置标题文本的方法。
3、在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.TitleView android:id="@+id/title_bar" android:layout_width="match_parent" android:layout_height="wrap_content" > </com.example.test.TitleView> </LinearLayout>
4、在MainActivity中获取自定义的标题栏,并且为返回按钮添加自定义点击事件:
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 { // Definisce il pennello private Paint mPaint; // Utilizzato per ottenere le dimensioni del testo private Rect mBounds; // Contatore, ogni volta che si clicca su questo controllo, il suo valore aumenta di 1 private int mCount; public CounterView(Context context, AttributeSet attrs) { super(context, attrs); // Inizializza il pennello e Rect mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBounds = new Rect(); // L'evento di clic del controllo setOnClickListener(this); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); // Disegna un rettangolo riempito di blu canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); mPaint.setColor(Color.YELLOW); mPaint.setTextSize(50); String text = String.valueOf(mCount); // Ottieni le dimensioni del testo mPaint.getTextBounds(text, 0, text.length(), mBounds); float textWidth = mBounds.width(); float textHeight = mBounds.height(); // Disegna la stringa canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mPaint); } @Override public void onClick(View v) { mCount++; // Ridisegna invalidate(); } }
2、In activity_main.xml includi questo layout personalizzato:
<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; // Il pulsante di eliminazione attuale è visibile? private boolean isDeleteShown; public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); // Creare l'oggetto ascoltatore dei gesti mGestureDetector = new GestureDetector(getContext(), this); // Ascoltare l'evento onTouch setOnTouchListener(this); } // Impostare l'evento di ascolto di eliminazione public void setOnDeleteListener(OnDeleteListener listener) { mOnDeleteListener = listener; } // Evento di ascolto del tocco @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) { // Se il pulsante di eliminazione attuale non è visibile e la velocità di scorrimento in direzione X è maggiore della velocità di scorrimento in direzione 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、定义列表项布局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); } // Inizializzazione della lista dei contenuti private void initContentList() { for (int i = 0; i < 20; i++) { contentList.add("Item di contenuto" + i); } } @Override public void onBackPressed() { if (mCustomLv.isDeleteShown()) { mCustomLv.hideDelete(); return; } super.onBackPressed(); } }
7, l'effetto di esecuzione è il seguente:
Questo è tutto il contenuto dell'articolo, speriamo che sia utile per la tua apprendimento e che tu sostenga fortemente il tutorial URL.
Dichiarazione: il contenuto di questo articolo è stato raccolto da Internet, il diritto d'autore appartiene al proprietario originale, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, il sito web non possiede il diritto di proprietà, non è stato elaborato manualmente e non assume alcuna responsabilità legale. Se trovi contenuti sospetti di violazione del copyright, è possibile inviare una e-mail a notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare e fornire prove pertinenti. Una volta verificata, il sito web rimuoverà immediatamente il contenuto sospetto di violazione del copyright.