第三章 UI开发
使用XML编写程序界面
可视化编辑工具并不利于你去真正了解界面后面的实现原理,通过这种方式制作出的界面通常不具有很好的屏幕适配性,而且当需要编写较为复杂的界面时,可视化编辑工具很难胜任。
等你完全掌握了使用XML来编写界面的方法后,不管是进行高复杂度的界面实现,还是分析和修改当前现有的界面,对你来说都将是手到擒来。
常见控件的使用方法和属性
TextView
android:layout_width=控件的宽度
android:layout_height=控件的宽度
android:text显示的文本内容
android:gravity控件内文字对齐方式
android:textSize文字大小
android:textColor文字的颜色
android:gravity指定文字对齐方式(center/bottom/left/right/top)
Button
按钮的点击事件
android:textAllCaps英文字母自动进行大写转换
EditText
android:hint提示性文本
android:maxLines最大行数
点击事件的应用
editText.getText().toString()代码中获取EditText中输入内容
ImageView
android:src图片地址
代码中改变图片内容imageView.setImageResource()
ProgressBar
设置控件的可见性setVisibility()—-View.VISIBLE/View.INVISIBLE/View.GONE
sytle样式修改
AlertDialog
通过AlertDialog.Builder创建出一个AlertDialog的实例
设置对话框的标题、内容、可否用back取消等属性,setPositiveButton()设置确定按钮的点击事件,setNegativeButton()设置取消按钮的点击事件。
调用show()方法显示对话框
ProgressDialog
通过ProgressDialog.Builder创建出一个ProgressDialog的实例
设置对话框的标题、内容、可否取消等。
调用show()方法显示对话框
setCancelable()中传入false则无法通过BACK键取消.dismiss()关闭对话框
ToggleButton
android:checked当前是否被选中
android:textOn/android:textOff
点击事件setOnCheckedChangeListener(),监听器onCheckedChanged()
CheckBox
android:checked当前是否被选中
点击事件setOnCheckedChangeListener(),监听器onCheckedChanged();
RadioGroup与RadioButton
RadioGroup是RadioButton的一个集合,提供多选一机制
android:orientation决定当前RadioGroup中RadioButton以什么形式排列
点击事件setOnCheckedChangeListener(),监听器选RadioGroup包中的onCheckedChanged();
四种基本布局
LinearLayout线性布局
android:orientation—-horizontal横向/vertical垂直
android:gravity指定文字在控件中的对齐方式
android:layout_gravity指定控件在布局中的对齐方式
android:weight使用比例的方式指定控件大小
RelativeLayout
android:alignParentLeft等
android:above/android:below
android:toLeftOf等
FrameLayout
- 所有空间都默认摆在布局的左上角,但是可以通过android:layout_gravity指定控件的对齐方式
PercentFrameLayout和PercentRelativeLayout
不能在使用wrap_content/match_parent指定控件大小,通过app:layout_widthPercent和app:layout_heightPercent指定控件大小
添加百分比库的依赖com.android.percent:……
自定义控件
新建一个title.xml布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/title_bg">
<Button
android:id="@+id/title_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:background="@drawable/back_bg"
android:text="Back"
android:textColor="#fff" />
<TextView
android:id="@+id/title_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:text="Title Text"
android:textColor="#fff"
android:textSize="24sp" />
<Button
android:id="@+id/title_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:background="@drawable/edit_bg"
android:text="Edit"
android:textColor="#fff" />
</LinearLayout>
引入布局
在activity_main.xml中添加
<include layout="@layout/title"/>
通过代码隐藏标题栏
ActionBar ac = getSupportActionBar();
if(ac != null){
ac.hide();
}
创建自定义控件
如果布局中有一些控件要求响应时间,我们还需要在每个活动中为这些控件单独编写一次事件注册代码,这种情况最好使用自定义控件的方式来解决
- 新建TitleLayout继承LinearLayout,并为里面的按钮添加点击事件,让它成为我们自定义的标题栏控件
public TitleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.title, this);
Button titleBack = (Button) findViewById(R.id.title_back);
Button titleEdit = (Button) findViewById(R.id.title_edit);
titleBack.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((Activity) getContext()).finish();
}
});
titleEdit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getContext(), "You clicked Edit button", Toast.LENGTH_SHORT).show();
}
});
}
- 在布局文件中添加这个自定义控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.uicustomviews.TitleLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
难点控件ListView
ListView的简单用法
布局中加入ListView控件,将需要展示的数组数据提供好,需要借助适配器(ArrayAdapter)传递数组数据给ListVIew
ArrayAdapter<String>adapter = new ArrayAdapter (Context context, @LayoutRes int resource, Listobjects);
ListView listView =(ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
ListView的定制界面用法
- 新建实体类Fruit作为ListView适配器的适配类型,Fruit类中有两个字段,name表示水果名称,imageId表示水果对应的图片资源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;}
}
- 为ListView的子项指定一个自定义的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp" />
</LinearLayout>
- 创建自定义的适配器,这个适配器继承自ArrayAdapter并将泛型指定为Fruit。重写了父类的一组构造函数,将上下文、ListView子项布局的id和数据都传递进来。重写getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。在getView()方法中首先通过getItem()方法得到当前项的Fruit实例,然后使用LayoutInflater来为资格子项加载我们传入的布局。
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position); // 获取当前项的Fruit实例
View view = = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
ImageView fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
ImageName fruitName = (TextView) view.findViewById (R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
}
- 布局中加入ListView控件,初始化水果数据(在Fruit类的构造函数中将水果的名称和图片id传入,然后把创建好的对象添加到水果列表中),创建FruitAdapter对象,并将FruitAdapter作为适配器传递给ListView
private List<Fruit> fruitList = new ArrayList<>();
initFruits();//初始化水果数据
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
- 提升ListView的运行效率
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId,
List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position); // 获取当前项的Fruit实例
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);
view.setTag(viewHolder); // 将ViewHolder存储在View中
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
}
- ListView的点击事件
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<Fruit>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
//注册按钮
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
//按钮的点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
}
...
}
RecyclerView用法
添加依赖库
compile 'com.android.support:recyclerview-v7:24.2.1'
布局中添加控件(使用和ListView一样的Fruit类和fruit_item.xml,代码略)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
准备一个适配器FruitAdapter继承RecyclerView.Adapter(重写
onCreateViewHolder()/onBindViewHolder()/getItemCount()
三个方法)并将泛型指定为FruitAdapter.ViewHolder,ViewHolder是FruitAdapter中定义的一个内部类。
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView fruitImage;
TextView fruitName;
//ViewHolder构造函数中,传入的View参数通常就是RecyclerView子项的最外层布局
public ViewHolder(View view) {
super(view);
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
//用于创建ViewHolder实例,并把加载处理的布局传入到函数当中,最后将ViewHolder的实例返回
@Override
public 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;
}
//该方法用于对RecyclerView的子项进行复制,会在每个子项被滚动到屏幕内的时候执行,通过position参数得到当前项的Fruitshili,然后在将数据设置到ViewHolder的ImageView和TextView当中
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
//用于告诉RecyclerView一共有多少子项,直接返回数据源的长度就可以
@Override
public int getItemCount() {
return mFruitList.size();
}
}
- 使用initFruits()方法初始化水果数据
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<Fruit>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//先使用initFruits()方法初始所有的水果数据
initFruits();
//接着获取到RecyclerView的实例
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//然后创建一个LinerLayoutManager的对象,并将它设置到RecyclerView当中,
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
//创建FruitAdapter的实例,并将水果数据传入到FruitAdapter的构造函数中
FruitAdapter adapter = new FruitAdapter(fruitList);
//完成最后适配器设置
recyclerView.setAdapter(adapter);
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
fruitList.add(mango);
}
}
}
- 横向滚动(关键代码)–改布局文件,然后在代码中再设置布局文件的排列方向
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL)
- 瀑布流布局(关键代码)–创建StaggeredGridLayoutManager的实例,在构造函数中传入两个参数,第一个参数用于指定布局的列数,第二个参数用于指定布局的排列方向
StaggeredGridLayoutManager LayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL)
- RecyclerView的点击事件
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder {
View fruitView;//添加fruitView变量来保存子项最外层的实例
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View view) {
super(view);
fruitView = view;//构造函数中注册
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
@Override
public 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);//创建ViewHolder的实例
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(), "you clicked view " + fruit.getName(), Toast.LENGTH_SHORT).show();
}
});//RecyclerView中TextView的点击事件
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(), "you clicked image " + fruit.getName(), Toast.LENGTH_SHORT).show();
}
});//RecyclerView中的ImageView的点击事件
return holder;
}
......
......
}