关于Layout的总结。
CoordinatorLayout简介
CoordinatorLayout是在 Google IO/15 大会发布的,遵循Material 风格,包含在 support Library中,结合AppbarLayout, CollapsingToolbarLayout等 可 产生各种炫酷的效果
简单来说就是
- 作为最上层的View
- 作为一个 容器与一个或者多个子View进行交互
类型 | 说明 |
---|---|
int SCROLL_FLAG_ENTER_ALWAYS | When entering (scrolling on screen) the view will scroll on any downwards scroll event, regardless of whether the scrolling view is also scrolling. |
int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED | An additional flag for 'enterAlways' which modifies the returning view to only initially scroll back to it's collapsed height. |
int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED | When exiting (scrolling off screen) the view will be scrolled until it is 'collapsed'. |
int SCROLL_FLAG_SCROLL | The view will be scroll in direct relation to scroll events. |
int SCROLL_FLAG_SNAP | Upon a scroll ending, if the view is only partially visible then it will be snapped and scrolled to it's closest edge. |
类型 | 说明 |
---|---|
int SCROLL_FLAG_ENTER_ALWAYS | W((entering) / (scrolling on screen))下拉的时候,这个View也会跟着滑出。 |
int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED | 另一种enterAlways,但是只显示折叠后的高度。 |
int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED | ((exiting) / (scrolling off screen))上拉的时候,这个View会跟着滑动直到折叠。 |
int SCROLL_FLAG_SCROLL | 这个View将会响应Scroll事件 |
int SCROLL_FLAG_SNAP | 在Scroll滑动事件结束以前 ,如果这个View部分可见,那么这个View会停在最接近当前View的位置 |
我们可以通过两种 方法设置这个Flag
- 方法一
setScrollFlags(int)
- 方法二
app:layout_scrollFlags="scroll|enterAlways"
注意事项
AppBarLayout必须作为CoordinatorLayout的直接子View,否则它的大部分功能将不会生效,如layout_scrollFlags等。
CollapsingToolbarLayout
CollapsingToolbarLayout继承与FrameLayout,官网地址,请自备梯子。
简单来说 ,CollapsingToolbarLayout是工具栏的包装器,它通常作为AppBarLayout的孩子。主要实现以下功能
- Collapsing title(可以折叠 的 标题 )
- Content scrim(内容装饰),当我们滑动的位置 到达一定阀值的时候,内容 装饰将会被显示或者隐藏
- Status bar scrim(状态栏布)
- Parallax scrolling children,滑动的时候孩子呈现视觉特差效果
- Pinned position children,固定位置的 孩子
下面我们一起来看一下几个常量
常量 | 解释说明 |
---|---|
int COLLAPSE_MODE_OFF | The view will act as normal with no collapsing behavior.(这个 View将会 呈现正常的结果,不会表现出折叠效果) |
int COLLAPSE_MODE_PARALLAX | The view will scroll in a parallax fashion. See setParallaxMultiplier(float) to change the multiplier used.(在滑动的时候这个View 会呈现 出 视觉特差效果 ) |
int COLLAPSE_MODE_PIN | The view will pin in place until it reaches the bottom of the CollapsingToolbarLayout.(当这个View到达 CollapsingToolbarLayout的底部的时候,这个View 将会被放置,即代替整个CollapsingToolbarLayout) |
我们有两种方法可以设置这个常量,
方法一:在代码中使用这个方法
setCollapseMode(int collapseMode)
方法 二:在布局文件中使用自定义属性
app:layout_collapseMode="pin"
DrawerLayout 简单使用
介绍
drawerLayout是Support Library包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产物。drawerLayout分为侧边菜单和主内容区两部分,侧边菜单可以根据手势展开与隐藏(drawerLayout自身特性),主内容区的内容可以随着菜单的点击而变化(这需要使用者自己实现)。
实用
DrawerLayout 实用比较简单,这里只介绍简单使用和常用API。
- 在布局文件中使用DrawerLayout 进行设置。如下示例代码:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dlyt_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
><!-- 内容 --><FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"><Button
android:id="@+id/btn_context"
android:text="context"
android:layout_width="match_parent"
android:layout_height="wrap_content" /></FrameLayout><!-- 左边菜单 --><FrameLayout
android:background="@color/colorAccent"
android:layout_gravity="start"
android:layout_width="200dp"
android:layout_height="match_parent"><Button
android:id="@+id/btn_left_menu"
android:text="left menu"
android:layout_width="match_parent"
android:layout_height="wrap_content" /></FrameLayout><!-- 右边菜单 --><FrameLayout
android:background="@color/colorAccent"
android:layout_gravity="end"
android:layout_width="200dp"
android:layout_height="match_parent"><Button
android:id="@+id/btn_right_menu"
android:text="right menu"
android:layout_width="match_parent"
android:layout_height="wrap_content" /></FrameLayout>
</android.support.v4.widget.DrawerLayout>
在 DrawerLayout 布局中有三个元素,
- view 主体
- 左边菜单:
左边菜单的设定使用android:layout_gravity="start"
设置。 - 右边菜单:
右边菜单的设定使用android:layout_gravity="end"
设置。
通过上面,我们就已经实现了左右划出菜单效果。
如果我们需要加上一些控制逻辑,可能就需要一些的一些API了。
DrawerLayout 中部分 API 介绍
- isDrawerOpen(@EdgeGravity int drawerGravity) : 判断菜单是否打开。
传入参数:
- GravityCompat.START : 左边菜单是否打开。
- GravityCompat.END : 右边菜单是否打开。
返回值
- 打开 : true
- 关闭 : false
- openDrawer(@EdgeGravity int gravity) : 打开菜单
传入参数:
- GravityCompat.START : 打开左边菜单。
- GravityCompat.END : 打开右边菜单。
- closeDrawer(@EdgeGravity int gravity) : 关闭菜单
传入参数:
- GravityCompat.START : 关闭左边菜单。
- GravityCompat.END : 关闭右边菜单。
- addDrawerListener(@NonNull DrawerListener listener) : 添加监听
- DrawerListener 类
- onDrawerSlide(View drawerView, float slideOffset) : 滑动时调用
- onDrawerOpened(View drawerView) : 打开菜单时调用
- onDrawerClosed(View drawerView) : 关闭菜单时调用
- onDrawerStateChanged(@State int newState) : 菜单状态改变时调用
其他API查看源码就会明白如何使用。
如下Java示例代码:
//添加监听
DrawerLayout.addDrawerListener(new DrawerViewListener());
private class DrawerViewListener implements DrawerLayout.DrawerListener {@Overridepublic void onDrawerSlide(View drawerView, float slideOffset) {//滑动时调用}@Overridepublic void onDrawerOpened(View drawerView) {//打开菜单时调用}@Overridepublic void onDrawerClosed(View drawerView) {//关闭菜单时调用}@Overridepublic void onDrawerStateChanged(int newState) {//菜单状态改变时调用}
}
AutoCompleteTextView常用属性
属性 | 描述 |
---|---|
android:completionHint | 设置出现在下拉菜单底部的提示信息 |
android:completionThreshold | 设置触发补全提示信息的字符个数 |
android:dropDownHorizontalOffset | 设置下拉菜单于文本框之间的水平偏移量 |
android:dropDownHeight | 设置下拉菜单的高度 |
android:dropDownWidth | 设置下拉菜单的宽度 |
android:singleLine | 设置单行显示文本内容 |
android:dropDownVerticalOffset | 设置下拉菜单于文本框之间的垂直偏移量 |
使用ArrayAdapter来作为AutoCompleteTextView的数据适配器
- 简单的xml布局
<AutoCompleteTextView
android:id="@+id/tv_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_type"
android:completionHint="@string/chint_recent"
android:completionThreshold="1" />
- 默认AutoCompleteTextView中的数据保存在SharedPreferences中,故将SharedPreferences做了简单的API封装以方便数据存取,详细的SharedPreferences请参考这里:SharedPreferences
// 从SharedPreferences中获取历史记录数据
private String getHistoryFromSharedPreferences(String key) {SharedPreferences sp = getSharedPreferences(SP_NAME, MODE_PRIVATE);return sp.getString(key, SP_EMPTY_TAG);
}// 将历史记录数据保存到SharedPreferences中
private void saveHistoryToSharedPreferences(String key, String history) {SharedPreferences sp = getSharedPreferences(SP_NAME, MODE_PRIVATE);SharedPreferences.Editor editor = sp.edit();editor.putString(key, history);editor.apply();
}// 清除保存在SharedPreferences中的历史记录数据
private void clearHistoryInSharedPreferences() {SharedPreferences sp = getSharedPreferences(SP_NAME, MODE_PRIVATE);SharedPreferences.Editor editor = sp.edit();editor.clear();editor.apply();
}
- 使用默认适配器的AutoCompleteTextView相关初始化
private void initSearchView() {mSearchTv = (AutoCompleteTextView) findViewById(R.id.tv_search);String[] mSearchHistoryArray = getHistoryArray(SP_KEY_SEARCH);mSearchAdapter = new ArrayAdapter<>(this,android.R.layout.simple_dropdown_item_1line,mSearchHistoryArray);mSearchTv.setAdapter(mSearchAdapter); // 设置适配器// 设置下拉提示框的高度为200dp// mAutoCompleteTv.setDropDownHeight(); // 或XML中为android:dropDownHeight="200dp"// 默认当输入2个字符以上才会提示, 现在当设置输入1个字符就自动提示// mAutoCompleteTv.setThreshold(1); // 或XML中为android:completionThreshold="1"// 设置下拉提示框中底部的提示// mAutoCompleteTv.setCompletionHint("最近的5条记录");// 设置单行输入限制// mAutoCompleteTv.setSingleLine(true);
}private String[] getHistoryArray(String key) {String[] array = getHistoryFromSharedPreferences(key).split(SP_SEPARATOR);if (array.length > MAX_HISTORY_COUNT) { // 最多只提示最近的50条历史记录String[] newArray = new String[MAX_HISTORY_COUNT];System.arraycopy(array, 0, newArray, 0, MAX_HISTORY_COUNT); // 实现数组间的内容复制}return array;
}
- 保存AutoCompleteTextView中的历史记录数据到SharedPreferences中
private void saveSearchHistory() {String text = mSearchTv.getText().toString().trim(); // 获取搜索框文本信息if (TextUtils.isEmpty(text)) { // null or ""Toast.makeText(this, "Please type something again.", Toast.LENGTH_SHORT).show();return;}String old_text = getHistoryFromSharedPreferences(SP_KEY_SEARCH);// 获取SP中保存的历史记录StringBuilder sb;if (SP_EMPTY_TAG.equals(old_text)) {sb = new StringBuilder();} else {sb = new StringBuilder(old_text);}sb.append(text + SP_SEPARATOR); // 使用逗号来分隔每条历史记录// 判断搜索内容是否已存在于历史文件中,已存在则不再添加if (!old_text.contains(text + SP_SEPARATOR)) {saveHistoryToSharedPreferences(SP_KEY_SEARCH, sb.toString()); // 实时保存历史记录mSearchAdapter.add(text); // 实时更新下拉提示框中的历史记录Toast.makeText(this, "Search saved: " + text, Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "Search existed: " + text, Toast.LENGTH_SHORT).show();}
}
上面代码中,为了能够实时更新下拉提示框中的历史记录,需要在保存数据后再调用ArrayAdapter.add()
方法,而不是调用ArrayAdapter.notifyDataSetChanged()
。
- 实时清除下拉提示框中的历史记录
clearHistoryInSharedPreferences(); // 试试清除历史记录
mSearchAdapter.clear(); // 实时清除下拉提示框中的历史记录
- 效果演示
使用自定义AutoCompleteAdapter来作为AutoCompleteTextView的数据适配器
- 简单的xml布局
使用RelativeLayout来容纳AutoCompleteTextView和ImageView,其中ImageView位于右侧,用于点击清除AutoCompleteTextView的内容
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"><AutoCompleteTextView
android:id="@+id/tv_custom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="12dp"
android:paddingEnd="40dp"
android:hint="@string/hint_type"/><ImageView
android:id="@+id/iv_custom"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="10dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:scaleType="fitCenter"
android:src="@drawable/ic_action_name"
android:contentDescription="@null"/>
</RelativeLayout>
- 使用自定义适配器的AutoCompleteTextView相关初始化
private void initCustomView() {mCustomTv = (AutoCompleteTextView) findViewById(R.id.tv_custom);mDeleteIv = (ImageView) findViewById(R.id.iv_custom);mDeleteIv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mCustomTv.setText(""); // 清空TextView的内容}});ArrayList<String> mOriginalValues = new ArrayList<>();String[] mCustomHistoryArray = getHistoryArray(SP_KEY_CUSTOM);mOriginalValues.addAll(Arrays.asList(mCustomHistoryArray)); // String[] => ArrayList<String>mCustomAdapter = new AutoCompleteAdapter(this, mOriginalValues);mCustomAdapter.setDefaultMode(AutoCompleteAdapter.MODE_STARTSWITH | AutoCompleteAdapter.MODE_SPLIT);// 设置匹配模式mCustomAdapter.setSupportPreview(true); // 支持使用特殊符号进行预览提示内容,默认为'@'simpleItemHeight = mCustomAdapter.getSimpleItemHeight();Toast.makeText(this, "simpleItemHeight: " + simpleItemHeight, Toast.LENGTH_SHORT).show(); // 103mCustomAdapter.setOnFilterResultsListener(new AutoCompleteAdapter.OnFilterResultsListener() {@Overridepublic void onFilterResultsListener(int count) {curCount = count;if (count > MAX_ONCE_MATCHED_ITEM) { // 限制提示框最多要显示的记录行数curCount = MAX_ONCE_MATCHED_ITEM;}if (curCount != prevCount) { // 仅当目前的数目和之前的不同才重新设置下拉框高度,避免重复设置prevCount = curCount;mCustomTv.setDropDownHeight(simpleItemHeight * curCount);}}});mCustomAdapter.setOnSimpleItemDeletedListener(new AutoCompleteAdapter.OnSimpleItemDeletedListener() {@Overridepublic void onSimpleItemDeletedListener(String value) {String old_history = getHistoryFromSharedPreferences(SP_KEY_CUSTOM); // 获取之前的记录String new_history = old_history.replace(value + SP_SEPARATOR, ""); // 用空字符串替换掉要删除的记录saveHistoryToSharedPreferences(SP_KEY_CUSTOM, new_history); // 保存修改过的记录}});mCustomTv.setAdapter(mCustomAdapter); //mCustomTv.setThreshold(1); //// 设置下拉时显示的提示行数 (此处不设置也可以,因为在AutoCompleteAdapter中有专门的事件监听来实时设置提示框的高度)// mCustomTv.setDropDownHeight(simpleItemHeight * MAX_ONCE_MATCHED_ITEM);
}
- 保存AutoCompleteTextView中的历史记录数据到SharedPreferences中
private void saveCustomHistory() {String text = mCustomTv.getText().toString().trim(); // 获取搜索框信息if (TextUtils.isEmpty(text)) { // null or ""Toast.makeText(this, "Please type something again.", Toast.LENGTH_SHORT).show();return;}String old_text = getHistoryFromSharedPreferences(SP_KEY_CUSTOM); // 获取SP中保存的历史记录StringBuilder sb;if (SP_EMPTY_TAG.equals(old_text)) {sb = new StringBuilder();} else {sb = new StringBuilder(old_text);}sb.append(text + SP_SEPARATOR); // 使用逗号来分隔每条历史记录// 判断搜索内容是否已存在于历史文件中,已存在则不再添加if (!old_text.contains(text + SP_SEPARATOR)) {saveHistoryToSharedPreferences(SP_KEY_CUSTOM, sb.toString()); // 实时保存历史记录mCustomAdapter.add(text); // 实时更新下拉提示框中的历史记录Toast.makeText(this, "Custom saved: " + text, Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "Custom existed: " + text, Toast.LENGTH_SHORT).show();}
}
- 实时清除下拉提示框中的历史记录
clearHistoryInSharedPreferences(); // 试试清除历史记录
mCustomAdapter.clear(); // 实时清除下拉提示框中的历史记录
- 自定义适配器AutoCompleteAdapter
AutoCompleteAdapter参考了ArrayAdapter的部分源代码,继承自BaseAdapter
并实现Filterable
接口,实现了以下功能:
- 实现自动补全的匹配模式的配置,有三种可选匹配模式:
MODE_CONTAINS / MODE_STARTSWITH(default) / MODE_SPLIT
- 实现匹配成功事件的回调,用于根据匹配结果数来动态设置下拉提示框的高度
- 实现删除匹配结果中子项的事件回调,用于实时更新存储在SharedPreferences的历史记录数据
- 支持使用
@
字符来预览所有提示内容
public class AutoCompleteAdapter extends BaseAdapter implements Filterable {private static final int MODE_NONE = 0x000; // 0000bpublic static final int MODE_CONTAINS = 0x001; // 0001bpublic static final int MODE_STARTSWITH = 0x002; // 0010bpublic static final int MODE_SPLIT = 0x004; // 0100bprivate static final String SPLIT_SEPARATOR = "[,.\\s]+"; // 分隔符,默认为空白符、英文逗号、英文句号private static boolean isFound = false; // 当MODE_STARTSWITH模式匹配成功时,不再进行MODE_SPLIT模式的匹配private int defaultMode = MODE_STARTSWITH; // 0110bprivate LayoutInflater inflater;private ArrayFilter mArrayFilter;private ArrayList<String> mOriginalValues; // 所有的itemprivate List<String> mObjects; // 过滤后的itemprivate final Object mLock = new Object(); // 同步锁private int maxMatch = 10; // 最多显示的item数目,负数表示全部private int simpleItemHeight; // 单行item的高度值,故需要在XML中固定父布局的高度值private char previewChar = '@'; // 默认字符private boolean isSupportPreview = false; // 是否可以使用@符号进行预览全部提示内容public AutoCompleteAdapter(Context context, ArrayList<String> mOriginalValues) {this(context, mOriginalValues, -1);}public AutoCompleteAdapter(Context context, ArrayList<String> mOriginalValues, int maxMatch) {this.mOriginalValues = mOriginalValues;// 初始化时将其设置成mOriginalValues,避免在未进行数据保存时执行删除操作导致程序的崩溃this.mObjects = mOriginalValues; this.maxMatch = maxMatch;inflater = LayoutInflater.from(context);initViewHeight();}private void initViewHeight() {View view = inflater.inflate(R.layout.simple_dropdown_item_1line, null);LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.layout_item);linearLayout.measure(0, 0);// 其他方法获取的高度值会因View尚未被绘制而获取到0simpleItemHeight = linearLayout.getMeasuredHeight();}public int getSimpleItemHeight() {return simpleItemHeight; // 5 * 2 + 28(dp) => 103(px)}public void setSupportPreview(boolean isSupportPreview){this.isSupportPreview = isSupportPreview;}public void setSupportPreview(boolean isSupportPreview, char previewChar){this.isSupportPreview = isSupportPreview;this.previewChar = previewChar;}@Overridepublic int getCount() {return mObjects.size();}@Overridepublic Object getItem(int position) {return mObjects.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder holder;if (convertView == null) {holder = new ViewHolder();convertView = inflater.inflate(R.layout.simple_dropdown_item_1line, null);holder.tv = (TextView) convertView.findViewById(R.id.tv_simple_item);holder.iv = (ImageView) convertView.findViewById(R.id.iv_simple_item);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.tv.setText(mObjects.get(position));holder.iv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String value = mObjects.remove(position);if (mDeleteListener != null) {mDeleteListener.onSimpleItemDeletedListener(value);}if (mFilterListener != null) {mFilterListener.onFilterResultsListener(mObjects.size());}mOriginalValues.remove(value);notifyDataSetChanged();}});return convertView;}private static class ViewHolder {TextView tv;ImageView iv;}public void setDefaultMode(int defaultMode) {this.defaultMode = defaultMode;}public void add(String item) {mOriginalValues.add(item);notifyDataSetChanged(); //}public void clear() {if(mOriginalValues != null && !mOriginalValues.isEmpty()) {mOriginalValues.clear();notifyDataSetChanged(); //}}// Interfacepublic interface OnSimpleItemDeletedListener {void onSimpleItemDeletedListener(String value);}private OnSimpleItemDeletedListener mDeleteListener;public void setOnSimpleItemDeletedListener(OnSimpleItemDeletedListener listener) {this.mDeleteListener = listener;}// Interfacepublic interface OnFilterResultsListener {void onFilterResultsListener(int count);}private OnFilterResultsListener mFilterListener;public void setOnFilterResultsListener(OnFilterResultsListener listener) {this.mFilterListener = listener;}@Overridepublic Filter getFilter() {if (mArrayFilter == null) {mArrayFilter = new ArrayFilter(mFilterListener);}return mArrayFilter;}private class ArrayFilter extends Filter {private OnFilterResultsListener listener;public ArrayFilter(OnFilterResultsListener listener) {this.listener = listener;}@Overrideprotected FilterResults performFiltering(CharSequence prefix) {FilterResults results = new FilterResults();if (mOriginalValues == null) {synchronized (mLock) {mOriginalValues = new ArrayList<>(mObjects);}}if (prefix == null || prefix.length() == 0) {synchronized (mLock) {ArrayList<String> list = new ArrayList<>(mOriginalValues);results.values = list;results.count = list.size();}} else {if (isSupportPreview) {int index = prefix.toString().indexOf(String.valueOf(previewChar));if (index != -1) {prefix = prefix.toString().substring(index + 1);}}String prefixString = prefix.toString().toLowerCase(); // prefixStringfinal int count = mOriginalValues.size(); // countfinal ArrayList<String> newValues = new ArrayList<>(count); // newValuesfor (int i = 0; i < count; i++) {final String value = mOriginalValues.get(i); // valuefinal String valueText = value.toLowerCase(); // valueText// 1. 匹配所有if ((defaultMode & MODE_CONTAINS) != MODE_NONE) {if (valueText.contains(prefixString)) {newValues.add(value);}} else { // support: defaultMode = MODE_STARTSWITH | MODE_SPLIT// 2. 匹配开头if ((defaultMode & MODE_STARTSWITH) != MODE_NONE) {if (valueText.startsWith(prefixString)) {newValues.add(value);isFound = true;}}// 3. 分隔符匹配,效率低if (!isFound && (defaultMode & MODE_SPLIT) != MODE_NONE) {final String[] words = valueText.split(SPLIT_SEPARATOR);for (String word : words) {if (word.startsWith(prefixString)) {newValues.add(value);break;}}}if(isFound) { // 若在MODE_STARTSWITH模式中匹配,则再次复位进行下一次判断isFound = false;}}if (maxMatch > 0) { // 限制显示item的数目if (newValues.size() > maxMatch - 1) {break;}}} // for (int i = 0; i < count; i++)results.values = newValues;results.count = newValues.size();}return results;}@Overrideprotected void publishResults(CharSequence constraint, FilterResults results) {//noinspection uncheckedmObjects = (List<String>) results.values;if (results.count > 0) {// 由于当删除提示框中的记录行时,而AutoCompleteTextView此时内容又不改变,故不会触发FilterResults事件// 导致删除记录行时,提示框的高度不会发生相应的改变// 解决方法:需要在ImageView的点击监听器中也调用OnFilterResultsListener.onFilterResultsListener()// 来共同完成if (listener != null) {listener.onFilterResultsListener(results.count);}notifyDataSetChanged();} else {notifyDataSetInvalidated();}}}
}
- 下拉提示框的item布局simple_dropdown_item_1line.xml
这里需要固定父类控件LinearLayout的高度,在AutoCompleteAdapter中会获取其高度用于设置AutoCompleteTextView的下拉菜单的高度
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_item"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="28dp"
android:padding="5dp"
android:gravity="center_vertical"><TextView
android:id="@+id/tv_simple_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingStart="5dp"
android:paddingEnd="0dp"
android:text="@string/text_nothing"
android:textAllCaps="false"
android:textSize="18sp"
android:textColor="#000"/><ImageView
android:id="@+id/iv_simple_item"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginEnd="5dp"
android:src="@drawable/ic_action_name"
android:contentDescription="@null"
android:scaleType="fitCenter" />
</LinearLayout>
- 效果演示
- 支持使用
@
字符来预览所有提示内容
关于Layout的总结。相关推荐
- android layout组件,Android UI学习 - Linear Layout, RelativeLayout
1.一些常用的公共属性介绍 1) layout_width -宽 fill_parent: 宽度和父元素相同,wrap_content: 宽度随本身的内容所调整,或者指定 px值来设置宽 2) lay ...
- android的布局流程,Android View 布局流程(Layout)全面解析
前言 上一篇文章,笔者详细讲述了View三大工作流程的第一个,Measure流程,如果对测量流程还不熟悉的读者可以参考一下上一篇文章.测量流程主要是对View树进行测量,获取每一个View的测量宽高, ...
- 更新pcb封装导入_教你如何将 AD 或 PADS 的原理图导入 Allegro 做 Layout
作为allegro专业layout的我们,由于设计软件的限制,有时候客户提供的原理图可能是AD或Power Logic绘制的,而AD或Power Logic的原理图转成orcad原理图后可能存在丢失网 ...
- android layout analyze
adapter api entity handler ui until widget appContent.java ************************************* 图片: ...
- iOS SwiftUI篇-3 排版布局layout
iOS SwiftUI篇-3 排版布局layout swiftUI提供的layout有: ZStack.GeometryReader.HStack.LazyVGrid.LazyHStack.LazyH ...
- xx.xib: error: Illegal Configuration: Safe Area Layout Guide before iOS 9.0报错问题解决
之前是用xcode8.3.3创建的工程最近升级到Xcode9.0 遇见了这个问题 在Xcode 9.0以上 新建xib文件会报错 xx.xib: error: Illegal Configuratio ...
- enyo官方开发入门教程翻译一Layout之Fittables
Fittables Fittable包帮助你创建布局更合理的使用可用空间(app都有这样的布局需求,但使用以前的web技术很难做到这一点).个人观点,现在移动Web开发框架中有很多都支持自适应布局,如 ...
- O - Layout POJ - 3169(差分约束)
O - Layout POJ - 3169 参考 思路: 限制条件 : 最大距离不超过w d[v] - d[u] <= w; 最小距离超过w d[v] - d[u] >= w; 移项得 d ...
- 移动端适配之二:visual viewport、layout viewport和ideal viewport介绍
上一篇博文,可算把像素这个东西讲清楚了.在这篇博文里面,将继续介绍viewport相关的内容. 很多博客都会提到PPK所讲的三个viewport,有的讲的比较复杂,看的云里雾里,我这里也大概介绍一下, ...
- Springboot 使用thymeleaf模板layout布局
使用layout布局前应该在pom文件中导入thymeleaf(dialect)依赖:如下 <properties><project.build.sourceEncoding> ...
最新文章
- 限时福利:腾讯高级专家手把手教你打造 OCR 神器!
- Java Spring实现原理研究之Servlet initialization初始化过程
- Vue父组件使用子组件时,需要携带参数,函数内如何获取子组件给的值
- Struts2中s:iterator/s:iterator标签的使用:
- python帮助文档在哪_python文档在哪里
- java导出富文本到word_富文本编辑器内容实现word导出下载,请各位大神们指点,感激不尽...
- 计算机专业个人能力评估,计算机应用专业毕业生个人自我评价
- 创建mysql用户并在单个数据库上赋权
- mysql双机热备的配置步骤
- cygwin下各盘挂载点
- blender_(uv应用)................http://digitalman.blog.163.com/blog/static/23874605620174172058299/...
- 工具类记录之Guawa的Splitter
- 虚拟摄像头之DirectShow虚拟摄像头开发
- JavaScript学习笔记(6)变量
- 【Java】一个公司职员薪水管理系统(顺序表)
- 周末作业-循环练习题(2)
- Oracle--ORA-01775: 同义词的循环链
- 【c语言中的运算符】
- 日志规范——转自晓风轻专栏
- 搭建注册中心Eureka运行时报错:[ main] o.s.b.d.LoggingFailureAnalysisReporter :
热门文章
- pandas 文档自用3
- 文心一言 VS 讯飞星火 VS chatgpt (43)-- 算法导论5.4 7题
- Net-a-Porter Coach factory outlet
- 爱上一个叫史今的男人
- 面试6轮才发offer,拒绝入职后,竟被HR索赔2万元违约金,理由是“浪费公司时间和人力成本!”...
- xp系统文件夹无法访问拒绝访问解决方法
- Excel插件制作-面积图系列
- 解决英伟达Jetson平台使用Python时的出现“Illegal instruction(cpre dumped)”错误
- 为什么64位计算机CPU架构叫amd64
- Unity特殊文件夹