前言

在Android系统中,针对大量数据的展示,可以使用ListView以列表的形式的呈现。虽然现在ListView在很多地方都被RecyclerView取代了,但是在一些合适的场景中依旧有用武之地。本文将详细讲解ListView的使用和常用技巧。

基本使用

ListView的使用还是很简单的,重点在于数据由Adapter(适配器)提供的,ListView并不直接访问数据源。因此,可以将ListView的使用分为3步:

  1. 获得数据源(如数组,List等)
  2. 通过数据源建立适配器(如ArrayAdapter等)
  3. 为ListView设置适配器

使用系统提供的布局

针对一些简单的场景(如只需要展示字符串),使用系统提供的ArrayAdapter即可。ArrayAdapter使用数组或List作为数据源,常用的两个构造方法如下:

//resource:列表项的布局文件//objects:数据源public ArrayAdapter(Context context,@LayoutRes int resource,T[] objects);public ArrayAdapter(Context context,@LayoutRes int resource,List objects)

使用ListView的示例代码如下:

//初始化普通布局的ListViewString[] dataArray=new String[]{"coding","ending","CodingEnding","Github","coder","Android"};//1.建立数据源ArrayAdapter<String> normalAdapter=new ArrayAdapter<String>(this,        android.R.layout.simple_list_item_1,dataArray);//2.建立适配器normalListView.setAdapter(normalAdapter);//3.设置适配器

ArrayAdapter中使用的android.R.layout.simple_list_item_1是系统提供的布局文件,其实就是一个TextView。

效果截图:

监听点击事件

//监听单击事件normalListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {    @Override    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {        Log.i(TAG,"当前位置:"+position);        String msg= (String) parent.getAdapter().getItem(position);//获取选中对象        Toast.makeText(ListViewActivity.this,msg,Toast.LENGTH_SHORT).show();    }});//监听长按事件normalListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {    @Override    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {        Toast.makeText(ListViewActivity.this,"发生长按事件",Toast.LENGTH_SHORT).show();        return true;    }});

可以看到,在监听器中通过parent.getAdapter().getItem(position)获取选中对象。注意,这个方法的返回值是Object对象,因此需要进行强制转换。

相关属性

android:divider:设置ListView各项之间的分割线 [color或drawable资源]android:dividerHeight:分割线的高度android:headerDividersEnabled:是否绘制每个HeaderView后的分割线 [默认为true]android:footerDividersEnabled:是否绘制每个FooterView前的分割线 [默认为true]android:listSelector:设置列表项被选中时的效果 [color或drawable资源]android:fastScrollEnabled:是否在快速滑动的是否显示右侧的滑动块android:scrollbars:设置滑动条的展示方式 [horizontal|vertical|none]android:stackFromBottom:是否在初始状态时显示ListView的最底部。 [默认为false]

stackFromBottom这个属性需要简单解释一下:如果设置为true,那么打开ListView首先看到的就是最底部的内容,看起来就像是ListView已经滚动到了最后一行;如果设置为false,就和默认状态一样,首先看到第一行的内容。

自定义列表布局

如果需要展示的内容比较复杂(比如图片加文字),我们就应该自定义适配器,使用自己的布局去展示列表项。

基本步骤

首先,建立一个实体类Book:

public class Book {    private String name;    private int imageRes;//图片资源    public Book(String name, int imageRes) {        this.name = name;        this.imageRes = imageRes;    }    @Override    public String toString() {        return name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getImageRes() {        return imageRes;    }    public void setImageRes(int imageRes) {        this.imageRes = imageRes;    }}

接着,自定义一个布局文件(左侧图片,右侧文字),本例中命名为listview_custom_item.xml,代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_marginTop="4dp"    android:layout_marginBottom="4dp"    android:gravity="center_vertical">    <ImageView        android:id="@+id/book_image"        android:layout_width="45dp"        android:layout_height="45dp"        android:layout_marginLeft="8dp" />    <TextView        android:id="@+id/book_name"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginLeft="8dp"        android:textAllCaps="false"        android:textSize="16sp" />LinearLayout>

然后,通过继承BaseAdapter实现我们自己的适配器,本例中命名为StyleListViewAdapter

public class StyleListViewAdapter extends BaseAdapter{    private Context context;    private List dataList;    public StyleListViewAdapter(Context context, List dataList) {        this.context = context;        this.dataList = dataList;    }    @Override    public int getCount() {        return dataList.size();    }    @Override    public Object getItem(int position) {        return dataList.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        Book book=dataList.get(position);        View view= LayoutInflater.from(context).inflate(R.layout.listview_custom_item,parent,false);        ImageView bookImageView=view.findViewById(R.id.book_image);        TextView bookNameView=view.findViewById(R.id.book_name);        bookImageView.setImageResource(book.getImageRes());        bookNameView.setText(book.getName());        return view;    }}

可以看到,需要重写getCount、getItem、getItemId、getView这四个方法。此外,还要提供一个构造方法用于外界传入Context和数据源(本例中为List)。

使用ViewHolder提升运行效率

在实际使用中,通常会使用ViewHolder提升ListView的运行效率,这一方式将充分利用ListView中View的复用机制。

首先,在Adapter中建立一个静态内部类ViewHolder:

static class ViewHolder{    ImageView bookImageView;    TextView bookNameView;}

然后,修改Adapter中的getView方法,复用已有的View:

@Overridepublic View getView(int position, View convertView, ViewGroup parent) {    Book book=dataList.get(position);    ViewHolder viewHolder;    if(convertView==null){        convertView= LayoutInflater.from(context).inflate(                R.layout.listview_custom_item,parent,false);        viewHolder=new ViewHolder();        viewHolder.bookImageView=convertView.findViewById(R.id.book_image);        viewHolder.bookNameView=convertView.findViewById(R.id.book_name);        convertView.setTag(viewHolder);//存储ViewHolder    }else{//复用已有的View        viewHolder= (ViewHolder) convertView.getTag();    }    viewHolder.bookImageView.setImageResource(book.getImageRes());    viewHolder.bookNameView.setText(book.getName());    return convertView;}

最后,在代码中为ListView设置自定义的适配器即可,代码如下:

//初始化自定义布局的ListViewList dataList=new ArrayList<>();dataList.add(new Book("《小王子》",R.mipmap.ic_launcher));dataList.add(new Book("《资本论》",R.mipmap.ic_launcher));dataList.add(new Book("《三体》",R.mipmap.ic_launcher));StyleListViewAdapter styleAdapter=new StyleListViewAdapter(this,dataList);customListView.setAdapter(styleAdapter);

效果截图:

实现多布局列表

在实际使用中,列表项可能不止一种布局形式,典型的如通讯录列表就有联系人、标题(如A、B、C等)这两种形式的列表项。通过对Adapter的修改,可以通过ListView实现多布局列表。在这里,将介绍如何实现一个简单的多布局列表,最终的效果如下:

准备布局文件

在本例中,主要有两种列表项,即标题项和内容项。因此,准备两个对应的布局文件,分别命名为listview_multi_title.xmllistview_multi_item.xml,代码如下:

listview_multi_title.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="match_parent"    android:layout_height="match_parent">    <TextView        android:id="@+id/item_title"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_margin="6dp" />LinearLayout>

listview_multi_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_marginTop="4dp"    android:layout_marginBottom="4dp"    android:gravity="center_vertical">    <ImageView        android:id="@+id/item_image"        android:layout_width="45dp"        android:layout_height="45dp"        android:layout_marginLeft="8dp" />    <TextView        android:id="@+id/item_content"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginLeft="8dp"        android:textAllCaps="false"        android:textSize="16sp"        android:textColor="#000000"/>LinearLayout>

准备实体类

对于不同的布局而言,应该使用不同的实体类。在本例中,有两种列表项,因此需要两个实体类。首先可以建立一个基类,本例中命名为BaseMultiBean,代码如下:

public abstract class BaseMultiBean {    public static final int TYPE_TITLE=0;//标题项    public static final int TYPE_ITEM=1;//内容项    protected int type;//类型    public int getType() {        return type;    }    public void setType(int type) {        this.type = type;    }}

可以看到,基类中主要是封装了实体的类型属性,这一属性将用于确定要使用的列表项布局。然后,再建立两个继承自基类的实体类,分别对应标题项和内容项,本例中命名为TitleBeanItemBean,代码如下:

TitleBean

public class TitleBean extends BaseMultiBean{    private String title;    public TitleBean(String title) {        this.title = title;        this.type=TYPE_TITLE;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }}

ItemBean

public class ItemBean extends BaseMultiBean{    private int imageRes;//图片资源    private String content;//内容    public ItemBean(int imageRes, String content) {        this.imageRes = imageRes;        this.content = content;        this.type=TYPE_ITEM;    }    public int getImageRes() {        return imageRes;    }    public void setImageRes(int imageRes) {        this.imageRes = imageRes;    }    public String getContent() {        return content;    }    public void setContent(String content) {        this.content = content;    }}

创建适配器

有了布局和实体类,就可以开始着手创建适配器了,本例中命名为MultiListViewAdapter。和前面提到的适配器相比,还需要实现getViewTypeCountgetItemViewType这两个方法。此外,getView也需要修改,以及还要提供两种不同的ViewHolder分别对应两种列表项。示例代码如下:

public class MultiListViewAdapter extends BaseAdapter{    ......    @Override    public int getViewTypeCount() {//返回类型种类数        return 2;    }    @Override    public int getItemViewType(int position) {//返回当前项的类型        BaseMultiBean bean=dataList.get(position);        return bean.getType();    }    static class TitleViewHolder{//针对标题项的复用        TextView titleView;    }    static class ItemViewHolder{//针对内容项的复用        ImageView itemImageView;        TextView itemContentView;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        TitleViewHolder titleViewHolder;        ItemViewHolder itemViewHolder;        switch(getItemViewType(position)){//根据Item的类型不同,执行相应的操作            case BaseMultiBean.TYPE_TITLE:                TitleBean titleBean= (TitleBean) dataList.get(position);                if(convertView==null){                    convertView=inflater.inflate(R.layout.listview_multi_title,parent,false);                    titleViewHolder=new TitleViewHolder();                    titleViewHolder.titleView=convertView.findViewById(R.id.item_title);                    convertView.setTag(titleViewHolder);                }else{                    titleViewHolder= (TitleViewHolder) convertView.getTag();                }                titleViewHolder.titleView.setText(titleBean.getTitle());                break;            case BaseMultiBean.TYPE_ITEM:                ItemBean itemBean= (ItemBean) dataList.get(position);                if(convertView==null){                    convertView=inflater.inflate(R.layout.listview_multi_item,parent,false);                    itemViewHolder=new ItemViewHolder();                    itemViewHolder.itemImageView=convertView.findViewById(R.id.item_image);                    itemViewHolder.itemContentView=convertView.findViewById(R.id.item_content);                    convertView.setTag(itemBean);                }else{                    itemViewHolder= (ItemViewHolder) convertView.getTag();                }                itemViewHolder.itemImageView.setImageResource(itemBean.getImageRes());                itemViewHolder.itemContentView.setText(itemBean.getContent());                break;            default:break;        }        return convertView;    }}

为ListView设置适配器

有了前面三步的准备工作,现在就可以着手为ListView设置适配器了,示例代码如下:

//初始化多状态布局的ListView(未设置点击监听)List multiDataList=new ArrayList<>();multiDataList.add(new TitleBean("第一个区域"));multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《小王子》"));multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《狮子王》"));multiDataList.add(new TitleBean("第二个区域"));multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《资本论》"));multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《三体》"));multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《孤独的进化者》"));MultiListViewAdapter multiAdapter=new MultiListViewAdapter(this,multiDataList);multiListView.setAdapter(multiAdapter);

常用技巧

设置空数据布局

public void setEmptyView(View emptyView);//在ListView的数据为空时显示emptyView

首先,在ListView所在的XML文件中定义一个EmptyView的布局,示例代码如下:

android:id="@+id/empty_view"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1">"wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center"    android:textSize="20sp"    android:text="暂无数据"/>

提示: EmptyView的widthheight属性可以和ListView保持一致,这样在空数据时刚好可以让EmptyView占据ListView的空间。

然后,在代码中为ListView设置EmptyView,示例代码如下:

View view=findViewById(R.id.empty_view);normalListView.setEmptyView(view);

效果截图:

隐藏滚动条

只需将android:scrollbars属性设置为none就可以隐藏ListView的滚动条。

去掉默认的选中颜色

只需将android:listSelector属性设置为#00000000就可以去掉默认的选中颜色(其实是设置为透明色)。

设置入场动画

android:layoutAnimation:为ListView设置布局动画。 [使用layoutAnimation资源]

为ListView设置layoutAnimation属性后,ListView的所有可见项都会执行指定的动画,有多少可见项就会执行多少次动画。主要的使用步骤如下:

首先,在res文件夹下的anim文件夹中建立一个set动画资源,本例中命名为listview_anim.xml,示例代码如下:

<set xmlns:android="http://schemas.android.com/apk/res/android">    <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="1000"/>    <translate android:fromXDelta="1000" android:toXDelta="0" android:duration="1000"/>set>

这个动画的作用是让View从右侧飞入,且有一个由浅入深的渐变效果,每个动画持续1000ms。

然后,在anim文件夹下建立一个layoutAnimation资源,本例中命名为listview_layout_animation.xml,示例代码如下:

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"    android:delay="0.3"    android:animationOrder="random"    android:animation="@anim/listview_anim">layoutAnimation>

delay指定下一次动画相对上一次动画的延迟倍数,可以是0到1之间的值;animation指定需要使用的动画资源。animationOrder指定子View的动画执行顺序,可选值与含义如下:

  1. normal:ListView的列表项顺序执行动画(从第一个可见列表项开始执行到最后一个可见列表项)
  2. random:ListView的列表项随机执行动画
  3. reverse:ListView的列表项逆序执行动画(从最后一个可见列表项开始执行到第一个可见列表项)

最后,为ListView指定对应的layoutAnimation资源即可:

<ListView    android:id="@+id/list_view_normal"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layoutAnimation="@anim/listview_layout_animation"/>

提示:如果需要手动执行布局动画,可以调用ListView的startLayoutAnimation方法。

效果截图:

增加列表头和列表尾

相关方法:

//data:为HeaderView绑定的数据(可通过ListAdapter#getItem方法获得)//isSelectable:指定HeaderView是否可选中(是否触发OnItemClickListener和OnItemLongClickListener)public void addHeaderView(View v, Object data, boolean isSelectable);//添加指定列表头(可调用多次,从上往下逐次添加)public void addHeaderView(View v);//添加指定列表头(可调用多次,从上往下逐次添加)public int getHeaderViewsCount();//获得列表头的个数public boolean removeHeaderView(View v);//移除列表头//data:为FooterView绑定的数据(可通过ListAdapter#getItem方法获得)//isSelectable:指定FooterView是否可选中((是否触发OnItemClickListener和OnItemLongClickListener))public void addFooterView(View v, Object data, boolean isSelectable);//添加指定列表头(可调用多次,从上往下逐次添加)public void addFooterView(View v);//添加指定列表头(可调用多次,从上往下逐次添加)public int getFooterViewsCount();//获得列表尾的个数public boolean removeFooterView(View v);//移除列表尾

说明:方法中的addHeaderView(View v)其实是通过调用addHeaderView(view, null, true)的方式实现的。addFooterView(View v)方法与之同理。

示例代码:

//为ListView添加列表头/尾String[] dataArray=new String[]{"coding","ending","CodingEnding","Github","coder","Android"};ArrayAdapter<String> headerFooterAdapter=new ArrayAdapter<String>(this,        android.R.layout.simple_list_item_1,dataArray);LayoutInflater inflater=LayoutInflater.from(this);View headerView=inflater.inflate(R.layout.listview_header,headerFooterListView,false);//实例化头布局View footerView=inflater.inflate(R.layout.listview_footer,headerFooterListView,false);//实例化尾布局headerFooterListView.addHeaderView(headerView,"HeaderView",true);//设置列表头可选中headerFooterListView.addFooterView(footerView,"FooterView",false);//设置列表尾不可选中headerFooterListView.setAdapter(headerFooterAdapter);

效果截图:

提示: addFooterView和addHeaderView应该在ListView使用setAdapter设置适配器前调用,否则可能出现异常。

注意:如果为ListView设置了HeaderView或者FooterView,在OnItemClickListener的onItemClick方法中,position可能不是我们希望取得的值(因为算上了HeaderView和FooterView的个数)。此时,如果想要获得选中项,应该通过AdapterView#getAdapter获取,示例代码如下:

customListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {    @Override    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {        Book book= (Book) parent.getAdapter().getItem(position);    }});

parent.getAdapter()获取的是ListView适配器的包装类,它的getItem方法会排除HeaderView和FooterView的影响,返回正确的对象。

动态增加列表项

先向数据源(如ArrayList)中添加数据,然后调用ListAdapter#notifyDataSetChanged方法通知列表刷新。示例代码如下:

List<Book> dataList dataList=new ArrayList<>();.....dataList.add(new Book("《新的书籍》",R.mipmap.ic_launcher));//为数据源新增数据styleAdapter.notifyDataSetChanged();//通知ListView数据已更新

具体代码请参考demo。

设置列表的滚动模式

android:transcriptMode:设置列表的滚动模式

ListView的滚动模式由transcriptMode属性决定,它有三种可选值,含义如下:

  1. disabled:列表有新的数据增加时,ListView并不发生滚动。[默认值]
  2. normal:如果最后一个列表项在可视范围内,当有新的数据增加时,列表会自动滚动到底部。
  3. alwaysScroll:只要有新的数据增加时,列表就会自动滚动到底部。

在开发中根据实际需求选择相应的滚动模式即可。

跳转到指定位置

//跳转到指定位置(让这个Item成为列表当前的第一个可见项)public void setSelection(int position);//让HeaderView成为列表当前的第一个可见项(如果HeaderView不存在则显示position为0的项)public void setSelectionAfterHeaderView();

小技巧:如果这两个方法在调用时无效,可以先调用ListView的clearFocus方法。

平滑滚动到指定位置

//平滑滚动到指定位置public void smoothScrollToPosition(int position);//平滑滚动n个列表项的距离//offset:需要滚动的列表项个数(offset为正数时ListView向上滚动,为负数时向下滚动)public void smoothScrollByOffset(int offset);

注意: smoothScrollToPosition并不保证将指定位置的列表项显示为列表当前的第一个可见项,只保证这个列表项在可视范围内。

效果截图:

监听滚动状态

只需要为ListView设置OnScrollListener即可,示例代码如下:

//监听ListView的滑动状态normalListView.setOnScrollListener(new AbsListView.OnScrollListener() {    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        //滑动状态发生变化时触发        //scrollState的可能值:[SCROLL_STATE_IDLE|SCROLL_STATE_TOUCH_SCROLL|SCROLL_STATE_FLING]    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        //滑动完成时触发        //firstVisibleItem:第一个可见项的索引值        //visibleItemCount:可见项的个数        //totalItemCount:列表项的总数    }});

onScrollStateChanged中的scrollState可能有三种取值,含义如下:

  1. SCROLL_STATE_IDLE:静止状态
  2. SCROLL_STATE_TOUCH_SCROLL:滑动状态(用户此时触碰着屏幕且在滑动)
  3. SCROLL_STATE_FLING:惯性滑动状态(用户此时未触碰屏幕,ListView借助上一次滑动的惯性滑动)

小技巧:可在OnScrollListener的onScroll方法中判断ListView是否已经滑动到末尾,示例代码如下:

@Overridepublic void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) {    if(totalItemCount>0&&firstVisibleItem+visibleItemCount==totalItemCount){        //已经滚动到末尾    }}

遍历列表当前所有的可见元素

for(int i=0;i<normalListView.getChildCount();i++){    View view=normalListView.getChildAt(i);//可以强制转换为具体的View}

常见问题

子控件抢夺焦点

问题描述:在自定义ListView列表项布局的时候,如果列表项中包含Button、CheckBox等需要焦点的控件,就可能导致点击列表项不起作用。

解决方案:

  1. 将列表项中Button、CheckBox等控件的android:focusable设置为false。
  2. 将列表项根布局的android:descendantFocusability属性设置为blocksDescendants。

descendantFocusability属性的可选值和效果说明如下:

  1. beforeDescendants:ViewGroup会在所有子View之前获得焦点
  2. afterDescendants:ViewGroup会在所有子View之后获得焦点
  3. blocksDescendants:ViewGroup会组织子View获得焦点

上面两种解决方案任选一种即可。

异步加载时图片显示错位

问题描述:如果列表项中的图片需要异步加载,由于ListView的View复用机制,在图片下载完毕时原来的ImageView可能已被复用,就可能导致图片显示错位。

解决方案:首先调用setTag方法为列表项中的ImageView设置标签。在异步加载完毕后,通过ListView的findViewWithTag方法查找ImageView。如果ImageView已经被复用了,这个方法的返回值就是null。通过判断这个方法的返回值是否为null,决定是否为ImageView设置图片资源,就可以解决图片显示错位的问题。示例代码如下:

@Overridepublic View getView(int position, View convertView, ViewGroup parent) {    .....    imageView.setTag(imageUrl);//将图片地址作为ImageView的Tag    .....}
ImageView imageView=listView.findViewWithTag(imageUrl);if(imageView!=null){    imageView.setImageDrawable(drawable);}

更多博客

《 Android UI GridView讲解》:详细讲解GridView的使用方法和常用技巧。
《 Android UI 常用控件讲解》:包括CheckBox、RadioButton、ToggleButton、Switch、ProgressBar、SeekBar、RatingBar、Spinner、ImageButton。

demo下载地址

https://github.com/CodingEnding/UISystemDemo [ 持续更新中 ]

参考资料

http://gundumw100.iteye.com/blog/1169065
http://blog.csdn.net/guolin_blog/article/details/45586553
http://blog.csdn.net/yangshangwei/article/details/50322919
http://blog.csdn.net/csdn_aiyang/article/details/70739945
http://blog.csdn.net/zhuwentao2150/article/details/52425334
http://blog.csdn.net/quwei3930921/article/details/51013012

更多相关文章

  1. CSDN精选Android开发博客
  2. Android学习笔记(16):绝对布局AbsoluteLayout、常用距离单位
  3. android简单demo学习系例之排版(TableLayout)[xml-based]
  4. Android布局的优化
  5. android ListView的使用方法
  6. Android(安卓)提高显示布局文件的性能[Lesson 2 - 使用include标
  7. android intent 及 intent action全面描述
  8. Android中fitsSystemWindows属性的用法总结
  9. Android(安卓)动态创建一个组件

随机推荐

  1. 解决MySQL因不能创建 PID 导致无法启动的
  2. 解决MySQL因不能创建临时文件而导致无法
  3. 快速在Windows上安装MySQL5.7压缩包
  4. MySQL服务自动停止的解决方法
  5. Mysql5.7在Centos6中的安装方法
  6. xtrabackup备份还原MySQL数据库
  7. mysql 5.7.5 m15 winx64.zip安装教程
  8. MySQL 启动成功但未监听端口的解决方法
  9. MySQL因配置过大内存导致无法启动的解决
  10. MySQL关闭密码强度验证功能