Android开发 入门篇(二) - 常用UI控件
文章目录
- 控件
- Button
- TextView
- EditText
- ImageView
- ProgressBar
- AlertDialog
- ProgressDialog
- 布局
- LenearLayout
- android:layout_gravity
- android:layout_weight
- RelativeLayout
- FrameLayout
- 百分比布局
- 其他
- 自定义控件
- ListView
- 定制ListView UI
- 优化
- 点击事件
- RecyclerView
- 横向滚动和瀑布流
- 点击事件
- 实践 -- 对话框UI
- OK,THANKS FOR READING.BYE BYE~
这次主要是控件。
出自《第一行代码(第二版)》,用于自己学(chao)习(shu)记录
控件
TextView、Button、EditText和ImageView这几个没啥好说的,比较简单。
Button
需要注意的是Button显示的英文都是默认大写的,需要设置属性android:textAllCaps=false就可以取消全部大写的设置了。
TextView
可以设置文字颜色、大小等,使用android:gravity属性设置文本的对齐方式。
EditText
通过android:hint设置默认显示的提示文字,点击文本框后消失。默认是文字如果超过一行的话会自动切换下一行,一直切换,如果设置android:maxLines属性,比如为2,则文本框最大就是两行,如果输入到第三行了,那么只会显示第2和3行,第一行就隐藏了。
ImageView
在属性中通过android:src="@drawble/xxx"设置图片资源,也可以在代码中通过imageview.setImageResource(R.drawble.xxx)进行设置。
ProgressBar
在xml中编写
<ProgressBarandroid:layout_width="match_parent"android:layout_height="wrap_content" />
即可设置一个进度条
但是在实际使用的时候进度条不是一直存在的,而是在我们需要的时候才会显示出来。
在Android中,所有控件都具有一个可见属性android:visiblity,可选值有三种visible、invisible和gone。默认为visible可见;invisible表示控件不可见,但是仍然保持原先的位置和大小,相当于透明了;gone表示不可见,也不占用任何屏幕空间。
也可以在代码中通过setVisibility()方法进行设置,参数值为View.INVISIBLE、View.INVISIBILITY和View.GONE。可以用getVisibility()方法获取控件的可见状态。
修改一下按钮的监听
Button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (MyProgressBar.getVisibility()==View.INVISIBLE)MyProgressBar.setVisibility(View.VISIBLE);elseMyProgressBar.setVisibility(View.INVISIBLE);}});
启动程序,按一次按钮可以显示进度条,再按一次就藏了
可以修改进度条的样式,比如改为水平进度条并设置最大值
<ProgressBarandroid:id="@+id/ProgressBar"android:layout_width="match_parent"android:layout_height="wrap_content"style="?android:attr/progressBarStyleHorizontal"android:max="100"/>
同时修改一下监听,按一下按钮进度增加一点
Button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {MyProgressBar.setProgress(MyProgressBar.getProgress()+10);}});
AlertDialog
最常使用到的对话库昂,用于提示警告,比如在退出时询问是否保存数据等,主要流程为new一个对象 -> 设置标题信息 -> 设置按钮和监听。其中setCancelable()方法是设置能否通过返回键关闭对话框。
Button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {AlertDialog.Builder alert=new AlertDialog.Builder(FirstActivity.this);alert.setTitle("This is a title");alert.setMessage("this is a message");alert.setCancelable(false);alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});alert.show();}});
ProgressDialog
这个对话框会显示一个进度条,一般用于让用户等待的时候,大体设置和AlertDialog差不多。
如果设置了setCancelable(false),表示无法使用返回键取消该对话框,需要在耗时事件结束后主动调用对话框的dismiss()方法销毁对话框。
布局
LenearLayout
线性布局可以通过android:orientation设置vertical还是horizontal。
要注意一下,如果设置vertical的话,高度就不能match_parent了,horizontal的话,宽度就不能match_parent了。
android:layout_gravity
指定布局中控件的对齐方式,如果是horizontal的话,只有垂直的对齐起作用,水平没用;如果是vertical的话,只有水平的对齐起作用,垂直没用。
android:layout_weight
按照比例指定控件的大小,在程序对不同手机的适配上起着很大的作用。如果有两个控件,android:layout_weight均为1,且布局为horizontal,则每个控件的宽度均为50%。
也可以制定部分控件的权重,如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/Button_1"android:text="this is a button"android:layout_width="wrap_content"android:layout_height="wrap_content"/><EditTextandroid:layout_width="0dp"android:layout_height="wrap_content"android:hint="this is a textview"android:maxLines="2"android:layout_weight="1"/>
</LinearLayout>
RelativeLayout
通过相对定位的方式可以更加随意的确定控件的位置,这个布局方式有很多的属性可以设置,看个栗子吧
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/Button_1"android:text="this is a button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_alignParentBottom="true"/>
</RelativeLayout>
相对于父组件的位置的属性主要有android:layout_alignParentTop、android:layout_alignParentBottom、android:layout_alignParentRight、android:layout_alignParentTop和android:layout_centerInParent。
同时可以设置相对于控件的位置,属性比较多,有android_layout_above、android_layout_below、android_layout_toRightOf和android_layout_toLeftOf等,分别表示该控件位于上一个控件的上方、下方、右和左。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/Button_1"android:text="button 1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"/><Buttonandroid:id="@+id/Button_2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/Button_1"android:layout_toRightOf="@id/Button_1"android:text="button 2"/><Buttonandroid:id="@+id/Button_3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/Button_1"android:layout_toLeftOf="@id/Button_1"android:text="button 3"/>
</RelativeLayout>
效果如下
除了相对位置,还有一组是相对对齐的属性,android:layout_alignRight、android:layout_alignLeft、android:layout_alignTop和android:layout_alignBottom,分别是将该控件和目标控件右对齐、左对齐,顶部对齐和底部对齐。
FrameLayout
帧布局相对简单,同样应用场所也比较少,所有控件默认放置左上角。
栗子
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/Button_1"android:text="button 1"android:layout_width="wrap_content"android:layout_height="wrap_content"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="This is a TextView"/>
</FrameLayout>
可以看到如果不设置的话,所有控件都默认在左上角放置。
可以通过android:layout_gravity在帧布局中对控件位置进行设置。
百分比布局
在LinearLayout中可以通过android:weight按比例修改控件大小,其余两种布局则不行,因此Android引入了百分比布局方式,不需要使用wrap_parent或者match_parent,可以直接设置百分比数值。
但是这是新增的,为了在所有版本上可以使用,Android团队把百分比布局定义在supprt库中了,只需要在build.gradle中添加库依赖就可以保证兼容性了。
compile ‘com.android.support:percent:24.2.1’
最好这个版本和appcompat的版本一致。
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:text="button 1"android:layout_gravity="left|top"app:layout_widthPercent="50%"app:layout_heightPercent="50%"/><Buttonandroid:text="button 2"android:layout_gravity="left|bottom"app:layout_widthPercent="50%"app:layout_heightPercent="50%"/><Buttonandroid:text="button 3"android:layout_gravity="right|top"app:layout_widthPercent="50%"app:layout_heightPercent="50%"/><Buttonandroid:text="button 4"android:layout_gravity="right|bottom"app:layout_widthPercent="50%"app:layout_heightPercent="50%"/>
</android.support.percent.PercentFrameLayout>
需要先写app命名空间,app:layout_widthPercent和app:layout_heightPercent,由于android.support.percent.PercentFrameLayout是继承FrameLayout布局,因此所有控件也是默认放置左上角的,避免重叠,可以通过android:layout_gravity设置放置的上下左右。
其他
除此之外的布局还包括AbsoluteLayout和TableLayout等等,只不过用的非常少。
自定义控件
按钮卓中也可以自定义控件,下图为一些控件之间的继承关系
所有控件都是直接或间接继承View,所有布局都是直接或间接继承ViewGroup。View是Android中最基本的UI组件,在屏幕上绘制一块矩形区域,并相应这个区域上的事件。所有控件都是在View的基础上添加各自的功能,ViewGroup是一种特殊的View,可以包含很多View和ViewGroup,是用于防止控件和布局的容器。
在此进行一个实例,自定义一个标题栏,需要使用的只有两个Button和一个TextView,如果多个活动中都需要使用该标题栏,如果每个活动都一个一个写一遍会很麻烦,因此如果可以写成一个控件直接引用就会方便很多。
主要是设置一下三个控件的android:layout_gravity,把TextView的宽度设为android:layout_weight=“1”,这里用到的三个背景图都是图片素材
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/title_bg"><Buttonandroid:text="back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="5dp"android:background="@drawable/back_bg"/><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="Title"android:textSize="24sp"android:layout_gravity="center"android:gravity="center"/><Buttonandroid:text="edit"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="5dp"android:background="@drawable/edit_bg"/>
</LinearLayout>
在first_layout.xml中加一行,引入这个layout
<include layout="@layout/title"
之后在FirstActivity.java中去除原有的标题栏
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);ActionBar actionBar=getSupportActionBar();if (actionBar!=null)actionBar.hide();}
但是我的效果看起来好差啊
之后需要编写代码以响应控件中的事件,新建一个TitleLayout
类,继承自LinearLayout
,重写构造函数,表示在加载布局的时候会自动调用该构造方法,对标题栏自动加载。
public class TitleLayout extends LinearLayout {public TitleLayout(Context context, @Nullable AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.title,this);}
}
布局文件中需要修改一下,把引入布局改成自定义控件
<com.example.k.androidpractice_1.TitleLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"/>
简单设置一下监听,注意这里的上下文获取,不是单纯的this
public TitleLayout(Context context, @Nullable AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.title,this);//ListenButton BackButton=findViewById(R.id.BackButton);Button EditButton=findViewById(R.id.EditButton);BackButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {((Activity)getContext()).finish();}});EditButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(getContext(),"this is edit",Toast.LENGTH_SHORT).show();}});}
点击EDIT按钮可以看到有Toast弹出,说明事件响应成功。
ListView
是最常用的控件,可以通过滚动的方式显示屏幕以外的内容。
xml中添加一个简单控件代码就好了
<ListViewandroid:id="@+id/ListView_1"android:layout_width="match_parent"android:layout_height="match_parent"/>
ListView不能直接放置内容,需要通过适配器进行内容设置。利用从网上或者数据库中获得的数据,构造相应数组,建立适配器,给ListView传入适配器。其中android.R.layout.simple_list_item_1是安卓内置的一个layout的id,里面只有一个TextView。
private String[] data={"apple","banana","grape","beach","pea"};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);ArrayAdapter<String> Adapter=new ArrayAdapter<String>(FirstActivity.this,android.R.layout.simple_list_item_1,data);ListView MyListView=findViewById(R.id.ListView_1);MyListView.setAdapter(Adapter);}
定制ListView UI
列表中不可能只有一个TextView,这里让他每一项显示一个TextView和ImageView。
先新建一个Fruit类,方便保存水果的信息,比如名字和图片ID等
public class Fruit {private String Name;private int ImageID;public Fruit(String Name,int ImageID){this.Name=Name;this.ImageID=ImageID;}public String getName(){return Name;}public int getImageID(){return ImageID;}
}
新建fruit_item.xml,编写列表每一项的布局
这里LinearLayout的width和height需要设置为wrap_content,不能用match_parent,否则将一个屏幕只有一种水果。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/ImageView_1"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:id="@+id/TextView_1"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
</LinearLayout>
编写自定义的适配器,新建FruitAdapter类,继承自ArrayAdapter,重写构造函数,传入上下文、ListViiew子项布局的id和数据。重写getView()方法,这个方法在每个子项滚动到屏幕内的时候被调用,这个方法中先得到这一项的Fruit实例,然后加载布局,之后设置图片和文字,返回布局view。
public class FruitAdapter extends ArrayAdapter {private int ID;public FruitAdapter(@NonNull Context context, int resource, List<Fruit> Object) {super(context, resource, Object);ID=resource;}public View getView(int position,View ConvertView,ViewGroup parent){Fruit fruit=(Fruit)getItem(position);View view= LayoutInflater.from(getContext()).inflate(ID,parent,false);ImageView FruitImage=(ImageView)view.findViewById(R.id.ImageView_1);TextView FruitName=(TextView)view.findViewById(R.id.TextView_1);FruitImage.setImageResource(fruit.getImageID());FruitName.setText(fruit.getName());return view;}
}
修改FirstActivity中的代码,先初始化,初始化的时候就是把所有水果的名字和图片放到List里面方便传参,for两遍是为了多弄点数据,new一个我们自定义的适配器,获取ListView,传入适配器参数。
public class FirstActivity extends AppCompatActivity {private List<Fruit> FruitList=new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);initFruits();FruitAdapter Adapter=new FruitAdapter(FirstActivity.this,R.layout.fruit_item,FruitList);ListView MyListView=findViewById(R.id.ListView_1);MyListView.setAdapter(Adapter);}private void initFruits(){String[] ItemName={"apple","banana","orange","watermelon","pear","grape","pineapple","strawberry","cherry","mango"};int[] ItemImage={R.drawable.apple_pic,R.drawable.banana_pic,R.drawable.orange_pic,R.drawable.watermelon_pic,R.drawable.pear_pic,R.drawable.grape_pic,R.drawable.pineapple_pic,R.drawable.strawberry_pic,R.drawable.cherry_pic,R.drawable.mango_pic};for (int i=0;i<=1;i++){for (int j=0;j<ItemName.length;j++){Fruit Item=new Fruit(ItemName[j],ItemImage[j]);FruitList.add(Item);}}}
}
之后运行就可以看到有列表了
优化
上述的getView()方法在列表滚动的时候会全部重新加载,如果快速滚动的话效率会比较低,注意到getView()方法里面有一个参数是ConvertView,这个参数可以将之前加载的布局进行缓存,可以直接重用提高效率。
public View getView(int position,View ConvertView,ViewGroup parent){Fruit fruit=(Fruit)getItem(position);View view;if (ConvertView==null){view= LayoutInflater.from(getContext()).inflate(ID,parent,false);}elseview=ConvertView;ImageView FruitImage=(ImageView)view.findViewById(R.id.ImageView_1);TextView FruitName=(TextView)view.findViewById(R.id.TextView_1);FruitImage.setImageResource(fruit.getImageID());FruitName.setText(fruit.getName());return view;}
点击事件
通过OnItemClickListener监听单击事件。
RecyclerView
由于LIstView性能不是很好,而且只能用于垂直,不能水平滚动。
官方也更推荐RecyclerView。这个和百分比布局一样,也是新增的,需要添加依赖,同样最好版本相同
compile ‘com.android.support:recyclerview-v7:24.2.1’
修改一下布局的控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/RecyclerView_1"android:layout_width="match_parent"android:layout_height="match_parent"/>
</LinearLayout>
这里需要一个新的适配器,继承自RecyclerView.Adapter。先定义了一个内部类ViewHolder,方便处理两个控件实例,构造函数中传入View参数,通常是子项的最外层布局,可以从中获得TextView和ImageView。主类FruitAdapter的构造函数的参数是传入List数据。
继承之后需要重写三个方法,onCreateViewHolder()方法创建ViewHolder实例,将其中的Fruit对象放到布局里面加载出来,返回实例。
onBindViewHolder()方法可以给每个子项的数据赋值,在子项滚到屏幕内时执行,通过positon获得子项实例,然后设置数据到ViewHolder里面。
getItemCount()就是子项长度。
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {private List<Fruit> FruitList;static class ViewHolder extends RecyclerView.ViewHolder {ImageView MyImageView;TextView MyTextView;public ViewHolder(View view) {super(view);MyImageView = (ImageView) view.findViewById(R.id.ImageView_1);MyTextView = (TextView) view.findViewById(R.id.TextView_1);}}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);ViewHolder holder=new ViewHolder(view);return holder;}@Overridepublic void onBindViewHolder(FruitAdapter.ViewHolder holder, int position) {Fruit fruit=FruitList.get(position);holder.MyImageView.setImageResource(fruit.getImageID());holder.MyTextView.setText(fruit.getName());}@Overridepublic int getItemCount() {return FruitList.size();}public FruitAdapter(List<Fruit> FruitList){this.FruitList=FruitList;}
}
修改FirstActivity
public class FirstActivity extends AppCompatActivity {private List<Fruit> FruitList=new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);initFruits();RecyclerView MyRecyclerView=(RecyclerView)findViewById(R.id.RecyclerView_1);LinearLayoutManager LayoutManager=new LinearLayoutManager(this);MyRecyclerView.setLayoutManager(LayoutManager);FruitAdapter Adapter=new FruitAdapter(FruitList);MyRecyclerView.setAdapter(Adapter);}private void initFruits(){String[] ItemName={"apple","banana","orange","watermelon","pear","grape","pineapple","strawberry","cherry","mango"};int[] ItemImage={R.drawable.apple_pic,R.drawable.banana_pic,R.drawable.orange_pic,R.drawable.watermelon_pic,R.drawable.pear_pic,R.drawable.grape_pic,R.drawable.pineapple_pic,R.drawable.strawberry_pic,R.drawable.cherry_pic,R.drawable.mango_pic};for (int i=0;i<=1;i++){for (int j=0;j<ItemName.length;j++){Fruit Item=new Fruit(ItemName[j],ItemImage[j]);FruitList.add(Item);}}}
}
之后就成功了,只不过这个大小有点奇怪,原因应该是我的fruit_item.xml没写好,不管了
得管,问题大概是fruit_item里面的设置问题,LinearLayout需要将width设为wrap_parent,height设为wrap_content,控件属性都是wrap_content
横向滚动和瀑布流
实现横向滚动首先先将fruit_item.xml的布局改一下,需要上面是图片下面是名字,这样才好水平放置。
宽度设为100dp,若为match则可能会过度拉伸,wrap会有长有短不好看,因此固定长度。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="100dp"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/ImageView_1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"/><TextViewandroid:id="@+id/TextView_1"android:layout_width="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp"android:layout_height="wrap_content"/>
</LinearLayout>
之后修改FirstActivity.java代码的onCreate()方法,就只需要加一句即可LayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);initFruits();RecyclerView MyRecyclerView=(RecyclerView)findViewById(R.id.RecyclerView_1);LinearLayoutManager LayoutManager=new LinearLayoutManager(this);LayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);MyRecyclerView.setLayoutManager(LayoutManager);FruitAdapter Adapter=new FruitAdapter(FruitList);MyRecyclerView.setAdapter(Adapter);}
可以看到已经横向展示了
除了LinearLayoutManager,RecyclerView还提供了另外两种布局方式,分别是GridLayoutManager和StaggeredGridLayoutManager,后者可以实现瀑布流。
修改fruit_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="5dp"><ImageViewandroid:id="@+id/ImageView_1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"/><TextViewandroid:id="@+id/TextView_1"android:layout_width="wrap_content"android:layout_gravity="left"android:layout_marginTop="10dp"android:layout_height="wrap_content"/>
</LinearLayout>
修改FirstActivity.java的onCreate(),再添加一个随机生成文件名的函数,长度不定用来展示这个布局管理,当各个子项不一样高的时候更能体现瀑布流的特点。StaggeredGridLayoutManager的两个参数第一个表示三列,第二个表示垂直方向。
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);initFruits();RecyclerView MyRecyclerView=(RecyclerView)findViewById(R.id.RecyclerView_1);StaggeredGridLayoutManager LayoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);MyRecyclerView.setLayoutManager(LayoutManager);FruitAdapter Adapter=new FruitAdapter(FruitList);MyRecyclerView.setAdapter(Adapter);}
...
private String getRandomLengthName(String Name){Random random=new Random();int length=random.nextInt(20)+1;StringBuilder sb=new StringBuilder();for (int i=0;i<length;i++){sb.append(Name);}return sb.toString();}
然后运行
点击事件
RecyclerView的点击和ListView的点击实现不一样,没有现成的监听器方法之类的,需要对每一个view进行点击事件的注册,为什么要这么麻烦呢?如果子项中有按钮,我们只需要点击按钮的时候,ListView不能很好的满足这个需求,因此RecyclerView干脆删除了这个监听器。
修改FruitAdapter代码,在FruitAdapter类中添加一个View类型变量FruitView,在ViewHolder()构造方法中给FruitView赋值view,在ViewHolder()中进行监听注册
...
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {private List<Fruit> FruitList;static class ViewHolder extends RecyclerView.ViewHolder {ImageView MyImageView;TextView MyTextView;View FruitView;public ViewHolder(View view) {super(view);FruitView=view;MyImageView = (ImageView) view.findViewById(R.id.ImageView_1);MyTextView = (TextView) view.findViewById(R.id.TextView_1);}}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);final ViewHolder holder=new ViewHolder(view);holder.FruitView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int position=holder.getAdapterPosition();Fruit fruit=FruitList.get(position);Toast.makeText(v.getContext(),fruit.getName(),Toast.LENGTH_SHORT).show();}});holder.MyImageView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int position=holder.getAdapterPosition();Fruit fruit=FruitList.get(position);Toast.makeText(v.getContext(),fruit.getName(),Toast.LENGTH_SHORT).show();}});return holder;}
...}
可以看到,此时不仅是点击图片还是名字都会有Toast弹出,说明成功。
实践 – 对话框UI
首先需要知道如何制作Nine-Patch图片,是一种被特殊处理过的PNG图片,能够指定某些区域被拉伸,某些区域不被拉伸。
比如有这个图片
如果被拉伸了可能会是这个样子
而安卓中有一个工具是专门制作这种图片的,在Android SDK/tools/draw9patch.bat,在使用的时候需要将Android SDK/jre/bin添加到环境变量中,双击打开
然后把图片复制到drawable文件夹中,给LinearLayout设置background,不用添加控件,width设为match_parent,height为wrap_content,效果如下
开始写主界面
filename:first_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#d8d0d8"><android.support.v7.widget.RecyclerViewandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/InputMessage"android:layout_width="0dp"android:layout_height="wrap_content"android:hint="Input your message"android:maxLines="2"android:layout_weight="1"/><Buttonandroid:id="@+id/Send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Send"/></LinearLayout>
</LinearLayout>
定义消息的实体类Msg,其中Type变量用于标记该消息是接收的还是发送的。
filename:Msg.java
package com.example.k.androidpractice_1;/*** Created by kang on 2020/1/30.*/public class Msg {public static final int TYPE_RECEIVED=0;public static final int TYPE_SEND=1;private String Content;private int Type;public Msg(String Content,int Type){this.Content=Content;this.Type=Type;}public String getContent(){return Content;}public int getType(){return Type;}
}
编写RecyclerView的子项布局msg_items.xml,这里有两个Layout,分别是接收消息和发送消息的对话框,在代码中通过可见属性决定显示什么。
filename:mas_items.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><LinearLayoutandroid:id="@+id/Left"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/message_left"android:layout_gravity="left"><TextViewandroid:id="@+id/LeftMessage"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#fff"android:layout_gravity="center"/></LinearLayout><LinearLayoutandroid:id="@+id/Right"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/message_right"android:layout_gravity="right"><TextViewandroid:id="@+id/RightMessage"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#fff"android:layout_gravity="center"/></LinearLayout>
</LinearLayout>
又到了适配器,现在适配器有点上手了,在绑定数据的时候先判断是接收的还是发送的,设置对应的可见属性。
package com.example.k.androidpractice_1;import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;import java.util.ArrayList;
import java.util.List;/*** Created by kang on 2020/1/30.*/public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {private List<Msg> MyMessageList=new ArrayList<>();@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_items,parent,false);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {Msg Message=MyMessageList.get(position);if (Message.getType()==Msg.TYPE_RECEIVED){holder.LeftLayout.setVisibility(View.VISIBLE);holder.RightLayout.setVisibility(View.GONE);holder.LeftMessageTextView.setText(Message.getContent());}else{holder.LeftLayout.setVisibility(View.GONE);holder.RightLayout.setVisibility(View.VISIBLE);holder.RightMessageTextView.setText(Message.getContent());}}@Overridepublic int getItemCount() {return MyMessageList.size();}static class ViewHolder extends RecyclerView.ViewHolder{LinearLayout LeftLayout,RightLayout;TextView LeftMessageTextView,RightMessageTextView;public ViewHolder(View view){super(view);LeftLayout=view.findViewById(R.id.Left);RightLayout=view.findViewById(R.id.Right);LeftMessageTextView=view.findViewById(R.id.LeftMessage);RightMessageTextView=view.findViewById(R.id.RightMessage);}}public MsgAdapter(List<Msg> MessageList){MyMessageList=MessageList;}
}
然后在FirstActivity.java进行初始化,设置数据,主要就还是获取控件实例,设置适配器和监听,数据初始化。在发送消息后,调用MessageAdapter.ntifyItemChanged()
方法,在有消息的时候进行刷新。调用MessageRecyclerView.scrollToPosition()
方法将列表滚动到最后一项。
filename:FirstActivity.java
package com.example.k.androidpractice_1;import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;public class FirstActivity extends AppCompatActivity {private List<Msg> MessageList=new ArrayList<>();private EditText InputMessageEditText;private Button SendButton;private RecyclerView MessageRecyclerView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_layout);initMsgs();InputMessageEditText=findViewById(R.id.InputMessage);SendButton=findViewById(R.id.Send);MessageRecyclerView=findViewById(R.id.MessageList);LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);MessageRecyclerView.setLayoutManager(linearLayoutManager);final MsgAdapter MessageAdapter=new MsgAdapter(MessageList);MessageRecyclerView.setAdapter(MessageAdapter);SendButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String Content=InputMessageEditText.getText().toString();if (!"".equals(Content)){Msg Message=new Msg(Content,Msg.TYPE_SEND);MessageList.add(Message); //添加消息//MessageAdapter.notifyItemChanged(MessageList.size()-1); //有消息的时候,刷新MessageRecyclerView.scrollToPosition(MessageList.size()-1); //滚动到最后一项InputMessageEditText.setText("");}}});}private void initMsgs(){Msg Message1=new Msg("this is a message 1",Msg.TYPE_SEND);MessageList.add(Message1);Msg Message2=new Msg("this is a message 22222",Msg.TYPE_RECEIVED);MessageList.add(Message2);}
}
随后运行程序可以看到已经成功了
但是有一个问题,监听里面的MessageAdapter.notifyItemChanged(MessageList.size()-1);
这一行如果不注释掉会报错,而且报错内容似乎是控件自身
<font color="red">
E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.example.k.androidpractice_1, PID: 20929java.lang.NoClassDefFoundError: Failed resolution of: Landroid/support/v4/animation/AnimatorCompatHelper;at android.support.v7.widget.DefaultItemAnimator.resetAnimation(DefaultItemAnimator.java:515)at android.support.v7.widget.DefaultItemAnimator.animateAdd(DefaultItemAnimator.java:218)at android.support.v7.widget.SimpleItemAnimator.animateAppearance(SimpleItemAnimator.java:114)at android.support.v7.widget.RecyclerView.animateAppearance(RecyclerView.java:3528)at android.support.v7.widget.RecyclerView$4.processAppeared(RecyclerView.java:461)at android.support.v7.widget.ViewInfoStore.process(ViewInfoStore.java:249)at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3385)at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3135)at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3568)at android.view.View.layout(View.java:20672)at android.view.ViewGroup.layout(ViewGroup.java:6194)at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)at android.view.View.layout(View.java:20672)at android.view.ViewGroup.layout(ViewGroup.java:6194)at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)at android.widget.FrameLayout.onLayout(FrameLayout.java:261)at android.view.View.layout(View.java:20672)at android.view.ViewGroup.layout(ViewGroup.java:6194)at android.support.v7.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:443)at android.view.View.layout(View.java:20672)at android.view.ViewGroup.layout(ViewGroup.java:6194)at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)at android.widget.FrameLayout.onLayout(FrameLayout.java:261)at android.view.View.layout(View.java:20672)at android.view.ViewGroup.layout(ViewGroup.java:6194)at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)at android.view.View.layout(View.java:20672)at android.view.ViewGroup.layout(ViewGroup.java:6194)at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)at android.widget.FrameLayout.onLayout(FrameLayout.java:261)at com.android.internal.policy.DecorView.onLayout(DecorView.java:753)at android.view.View.layout(View.java:20672)at android.view.ViewGroup.layout(ViewGroup.java:6194)at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2792)at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2319)at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1460)at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7183)at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)at android.view.Choreographer.doCallbacks(Choreographer.java:761)at android.view.Choreographer.doFrame(Choreographer.java:696)at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)at android.os.Handler.handleCallback(Handler.java:873)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loop(Looper.java:193)at android.app.ActivityThread.main(ActivityThread.java:6669)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
E/AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "android.support.v4.animation.AnimatorCompatHelper" on path: DexPathList[[zip file "/system/framework/org.apache.http.legacy.boot.jar", zip file "/data/app/com.example.k.androidpractice_1-qLD9Df1Lh_NOfCKU_GrQ2w==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.k.androidpractice_1-qLD9Df1Lh_NOfCKU_GrQ2w==/lib/x86, /system/lib]]at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)at java.lang.ClassLoader.loadClass(ClassLoader.java:379)at java.lang.ClassLoader.loadClass(ClassLoader.java:312)... 52 more
</font>
I/Process: Sending signal. PID: 20929 SIG: 9
Application terminated.
这个问题我还没查是为什么,暂时就先不要这一行了。
Over
OK,THANKS FOR READING.BYE BYE~
Android开发 入门篇(二) - 常用UI控件相关推荐
- 安卓入门系列-07常用UI控件(长文)
常用UI控件 简介 这一篇介绍开发中的常用UI控件. 布局管理器 所有布局管理器都是ViewGroup的子类,都可作为容器类使用.继承自View,所以也可嵌套. 常见的布局之前已经提到了三种,这里不再 ...
- IOS 常用UI控件
目录 下拉刷新 模糊效果 AutoLayout 富文本 图表 表相关与Tabbar 隐藏与显示 HUD与Toast 对话框 其他UI 具体内容 下拉刷新 EGOTableViewPullRefresh ...
- android button imagebutton 区别,Android 开发入门篇
Button 与 ImageButton 本节学习Android基本控件按钮控件,Button和ImageButton用法基本类似,所以本节重点讲解Button控件. 在布局中添加Button控件: ...
- ios 设置属性的center_IOS开发-常用UI控件的基本使用(Transform形变属性、frame属性、center属性的使用)...
3. disabled(失效状态,不可用状态) 如果enabled属性为NO,就是处于disable状态,代表按钮不可以被点击,默认情况是可以点击的. 对应的枚举常量:UIControlStateDi ...
- 野人学Android基础篇之初探UI控件第一课--TextView动态赋值
除了上节课中讲到的TextView静态赋值,还有一种更加灵活的赋值方法–动态赋值.在app的运行过程中,根据程序的需要可以随时改变TextView的值. 其实现的基本逻辑可以归纳如下: 1.通过id获 ...
- FreeCAD二次开发:集成二维CAD控件MxDraw
济南友泉软件有限公司 FreeCAD是一套基于OpenCASCADE/QT的三维全参数化建模开源代码,虽然提供了Draft.TechDraw等二维绘图功能,但是其二维建模能力仍旧比较弱. Ref. f ...
- ArcGIS for Android Runtime100 基本操作(二)——地图控件的常见操作
以前我刚开始学习ArcGIS时候,看得最常见的一篇博客是Ersi中国官方写的一篇<ArcGIS for Android地图控件的5大常见操作>,地址是http://blog.csdn.ne ...
- Android自定义控件入门实践之雷达扫描控件
以前因为工作的关系,对于自定义控件用的少之又少,无非就是把几个控件放置到ViewGroup内部,然后提供开放方法,就成了一个所谓的自定义控件,但是这种小伎俩太简单,面试的时候这点东西根本Hold不住场 ...
- 一些常用UI控件汇总
1.标签控件 UIlable 作用:显示文本 常用属性: (1).lineBreakMode //label宽度不够时,对文本的打断方式,默认为打断文本尾部 (2).shadowColor //设置l ...
最新文章
- 进阶必备:CNN经典论文代码复现 | 附下载链接
- [置顶] 设计模式之创建类模式——原型模式
- 机房管理系列之文件服务器管理
- 课堂上的社死现场...
- 人脸识别屡遭非议,会成为“潘多拉魔盒”吗?
- 使用脚本创建查找修改销毁游戏对象
- 可长点心吧-sort
- 2.word转换为pdf
- linux下解压 编译 安装,Linux 下开发环境安装配置-编译、解压、超链、
- 系统学习机器学习之正则化(一)
- 如何将response里header的date转化为当地时间_将产品20元利润提升到2000元,靠的是卖体验!...
- matlab建模和仿真实验,MATLAB-Simulink系统建模与仿真-实验报告
- C#使用oledb连接excel执行Insert Into语句出现“操作必须使用一个可更新的查询”的解决办法
- 乡镇政府网络智能办公系统(乡镇OA)应用【乡镇信息化经验】
- 如何获取QQ邮箱授权码
- 工程经济学99分速成复习——第一章 绪论
- 【Java书笔记】:《Redis 深度历险:核心原理和应用实践》分布式锁,延时队列,位图,HyperLogLog,布隆过滤器,漏斗限流,GeoHash,Scan,管道,事务,主从,Redis源码
- matlab程序是什么格式,科学网—Matlab中的P代码文件 - 杨笔锋的博文
- ECSHOP V2.7.3文件目录结构
- 我发现一个地方能免费领取价值198元的手环,具有能量并且有高人加持过的,只要关注微信就可以免费领取
热门文章
- js中的上下文,好比煮一顿泡面
- CodeForces - 108A Palindromic Times
- win10应用商店linux_Windows 10 在安装Ubuntu(WSL)后UWP与应用商店全面闪退//The UWP - Microsoft Community...
- 《The Witness》:游戏中的建筑学(上)
- 【转】ASCII码十进制、十六进制对照表
- 环回接口(Loopback Interface)【转】
- busybox ync.c:(.text.sync_main+0x78): undefined reference to `syncfs' 出错
- Android自学之路,DrawerLayout must be measured with MeasureSpec.EXACTLY.错误
- [NOI2016]旷野大计算
- 皇帝内经:恬淡虚无,真气从之,精神内守,病安从来?