English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
J'ai utilisé de nombreux contrôles personnalisés dans mon projet de fin d'études et j'ai toujours eu l'intention de résumer les méthodes d'implémentation des contrôles personnalisés. Aujourd'hui, je vais le faire. Avant cela, j'ai lu plusieurs articles sur les vues personnalisées sur le blog de l'expert Guo Lin et j'ai beaucoup appris. Dans cet article, j'ai utilisé certaines de ces idées.
En résumé, l'implémentation des contrôles personnalisés comporte trois méthodes, à savoir : les composants combinés, les contrôles dessinés et les contrôles hérités. Nous allons présenter ces trois méthodes respectivement ci-dessous.
(I) Composants combinés
Les composants combinés, comme son nom l'indique, consistent à combiner plusieurs petits composants pour former un nouveau composant. Ces petits composants sont souvent des composants intégrés au système. Par exemple, les contrôles d'en-tête utilisés dans de nombreuses applications sont en fait des composants combinés. Alors, nous allons expliquer l'utilisation des composants combinés en implémentant un contrôle d'en-tête personnalisé simple.
1、Créez un nouveau projet Android, créez un fichier de mise en page personnalisé pour l'en-tête 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/arrière-plan1_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>
可见这个标题栏控件还是比较简单的,其中在左边有一个返回按钮,背景是一张事先准备好的图片back1_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中主要是为自定义的标题栏加载了布局,为返回按钮添加事件监听方法,并提供了设置标题文本的方法。
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, "Cliquez sur le bouton Retour", Toast.LENGTH_SHORT) .show(); finish(); } });
5、Effet de fonctionnement suivant :
De cette manière, un titre personnalisé a été réalisé par combinaison, et encore plus de combinaisons peuvent créer des contrôles personnalisés plus complexes, par exemple, une barre de recherche personnalisée.
(Deuxième partie) Contrôle dessiné automatiquement
Les contrôles dessinés automatiquement sont tous dessinés par eux-mêmes, dans la méthode onDraw de View. Voici un exemple de compteur simple, chaque fois que ce contrôle est cliqué, le nombre de comptage augmente1et le montrer.
1、Créer la classe CounterView, héritant de View et implémentant l'interface OnClickListener :
public class CounterView extends View implements OnClickListener { // Définir le pinceau private Paint mPaint; // Utilisé pour obtenir la largeur et la hauteur du texte private Rect mBounds; // Nombre de comptage, chaque fois que ce contrôle est cliqué, sa valeur augmente1 private int mCount; public CounterView(Context context, AttributeSet attrs) { super(context, attrs); // Initialiser le pinceau et le Rect mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBounds = new Rect(); // L'événement de clic de ce contrôle setOnClickListener(this); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); // Dessiner un rectangle rempli de bleu canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); mPaint.setColor(Color.YELLOW); mPaint.setTextSize(50); String text = String.valueOf(mCount); // obtenir la largeur et la hauteur du texte mPaint.getTextBounds(text, 0, text.length(), mBounds); float textWidth = mBounds.width(); float textHeight = mBounds.height(); // dessiner une chaîne de caractères canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mPaint); } @Override public void onClick(View v) { mCount ++; // redessiner invalidate(); } }
2introduire ce layout personnalisé dans 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、Effet de fonctionnement suivant :
(Troisième partie) Hériter de contrôles
c'est-à-dire hériter des contrôles existants, créer de nouveaux contrôles, conserver les caractéristiques des contrôleurs parents hérités, et peuvent également introduire de nouvelles caractéristiques. Voici un exemple d'implémentation de ListView personnalisé prenant en charge le glissement horizontal pour supprimer des éléments de liste.
1créer le layout de bouton supprimer delete_btn.xml, ce layout est affiché après un glissement horizontal sur une liste d'éléments :
<?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="supprimer" android:textColor="#FFFFFF" android:textSize="16sp" > </Button>
2créer la classe CustomListView, héritant de ListView et implémentant les interfaces OnTouchListener et OnGestureListener :
public class CustomListView extends ListView implements OnTouchListener, OnGestureListener { // détecteur de gestes private GestureDetector mGestureDetector; // Écouteur d'événement de suppression public interface OnDeleteListener { void onDelete(int index); } private OnDeleteListener mOnDeleteListener; // Bouton de suppression private View mDeleteBtn; // Agencement de l'élément de liste private ViewGroup mItemLayout; // Élément de liste sélectionné private int mSelectedItem; // Si le bouton de suppression actuel est affiché private boolean isDeleteShown; public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); // Créer un objet de détecteur de gestes mGestureDetector = new GestureDetector(getContext(), this); // Écouter l'événement onTouch setOnTouchListener(this); } // Définir l'événement d'écoute de suppression public void setOnDeleteListener(OnDeleteListener listener) { mOnDeleteListener = listener; } // événement d'écoute du tactile @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) { // Si le bouton de suppression actuel n'est pas affiché et que la vitesse de glissement dans l'axe x est supérieure à la vitesse de glissement dans l'axe 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; } // Masquer le bouton de suppression public void hideDelete() { mItemLayout.removeView(mDeleteBtn); mDeleteBtn = null; isDeleteShown = false; } public boolean isDeleteShown() { return isDeleteShown; } /** * Les autres méthodes ne sont pas utilisées dans cet exemple */ @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) { } }
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、Dans activity_main.xml, inclure le ListView personnalisé :
<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、Dans MainActivity, effectuer l'initialisation de la liste, définir l'événement de clic sur le bouton de suppression de l'élément de la liste, etc. :
public class MainActivity extends Activity { // Lv personnalisé private CustomListView mCustomLv; // Adapter personnalisé private CustomListViewAdapter mAdapter; // Liste de contenu 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); } // Initialiser la liste de contenu private void initContentList() { for (int i = 0; i < 20; i++) { contentList.add("élément de contenu" + i); } } @Override public void onBackPressed() { if (mCustomLv.isDeleteShown()) { mCustomLv.hideDelete(); return; } super.onBackPressed(); } }
7、Effet de fonctionnement suivant :
Voici le contenu intégral de cet article, j'espère que cela peut aider à votre apprentissage. J'espère aussi que vous soutiendrez le tutoriel Nécessité.
Déclaration : Le contenu de cet article est tiré d'Internet, propriété intellectuelle de l'auteur. Le contenu est apporté par les utilisateurs d'Internet et téléchargé spontanément. Ce site ne détient pas de droits de propriété, n'a pas été édité par l'homme et n'assume aucune responsabilité juridique. Si vous trouvez du contenu susceptible de violer les droits d'auteur, veuillez envoyer un e-mail à : notice#w3Déclaration : Le contenu de cet article est tiré d'Internet, propriété intellectuelle de l'auteur. Le contenu est apporté par les utilisateurs d'Internet et téléchargé spontanément. Ce site ne détient pas de droits de propriété, n'a pas été édité par l'homme et n'assume aucune responsabilité juridique. Si vous trouvez du contenu susceptible de violer les droits d'auteur, veuillez envoyer un e-mail à : notice#w