English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
В моем дипломном проекте много раз использовался пользовательский контрол, я всегда хотел подвести итоги способов реализации пользовательских контролей, сегодня я постараюсь сделать это. До этого я изучал несколько статей о пользовательских View на блоге Гуо Линя, и я чувствовал, что это было очень полезно, в этой статье я использовал некоторые из этих материалов.
В общем, реализация пользовательских контролей имеет три способа,分别是: компоновка контролей, рисованные контроли и наследование контролей. Ниже мы рассмотрим эти три способа.
(один)
Компоновка контролов, как следует из названия, это объединение некоторых小的 контролей в новый контрол, эти小的 контроли часто являются системными. Например, в большинстве приложений используется контрол заголовка, на самом деле используется компоновка контролей, поэтому ниже将通过 реализацию простого пользовательского контрола заголовка расскажем о χρήсе компоновки контролей.
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>
可见 этот контрол заголовка довольно прост, в левом углу есть кнопка возврата, фоном является предварительно подготовленное изображение back1_64.png, в центре заголовка находится текст заголовка.
2、Создайте класс TitleView, наследующийся от RelativeLayout:
public class TitleView extends RelativeLayout { // Контрол кнопки возврата private Button mLeftBtn; // Контрол текста заголовка 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, "Нажата кнопка возврата", Toast.LENGTH_SHORT) .show(); finish(); } });
5. Результат выполнения:
Таким образом, с помощью комбинирования можно реализовать пользовательскую заголовочную панель, и с помощью более сложного комбинирования можно создать пользовательские контроли с более сложными функциями, например, пользовательскую строку поиска.
(2) Пользовательский рисованный контрол
Содержимое пользовательских контролей рисуется вручную, в методе onDraw класса View. Ниже приведен пример простого счетчика, который при каждом нажатии увеличивает значение счетчика на 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; // Листener события удаления 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 e2, 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 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); } // Инициализация списка содержимого 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. Результат выполнения:
Вот и все, что есть в этой статье, я надеюсь, что это поможет вам в изучении, и希望大家多多支持呐喊教程。
Заявление: содержание этой статьи взято из Интернета, авторские права принадлежат соответствующему автору, контент предоставлен пользователями Интернета в добровольном порядке, сайт не обладает правами собственности, не underwent редактирование, и не несет ответственности за связанные с этим юридические последствия. Если вы обнаружите содержимое,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма, пожалуйста, замените # на @) для подачи жалоб и предоставления соответствующих доказательств. Если подтвердится, что это侵权内容, сайт немедленно удалят涉嫌侵权的内容。