这一部分书中讲的十分简洁,估计是因为作者考虑到大家刚刚接触到Android,所以没有展开。而控件的使用其实也是非常简单的事情,只要有Java的基础,再了解一些控件的属性,就可以得心应手了。所以笔者在这里补充一些控件的使用,也十分简单。如果暂时不能理解,也不要担心,先往下看,等到明白的时候在回过头来,就会发现真的特别简单。
TextView
属性
android:id="@+id/txv1"
当前控件唯一标识符,以后的交互中会经常使用,相当于这个控件的name
android:layout_width="match_parent"
android:layout_height="wrap_content"
控件的宽度和高度,每个控件都有,除了可以指定数据还可以使用match_ parent、 fill_ parent和wrap_ content。现在fill_ parent已经不用了。关于match_ parent和wrap_ content书中是这样讲的,我觉得还是挺简单明了的。
——第一行代码
然后使用android:layout_ width 和android: layout_ height 指定了控件的宽度和高度。Android 中所有的控件都具有这两个属性,可选值有3种: match_ parent、 fill_ parent和wrap_ content。 其中match_ parent 和fill_ parent 的意义相同,现在官方更加推荐使用match_ parent。 match_ parent 表示让当前控件的大小和父布局的大小-样,也就是由父布局来决定当前控件的大小。wrap_ content 表示让当前控件的大小能够刚好包含住里面的内容,也就是由控件内容决定当前控件的大小。所以上面的代码就表示让TextView的宽度和父布局一样宽,也就是手机屏幕的宽度,让TextView的高度足够包含住里面的内容就行。当然除了使用上述值,你也可以对控件的宽和高指定一个固定的大小,但是这样做有时会在不同手机屏幕的适配方面出现问题。
总结起来就是一句话,match_ parent是填充父布局,wrap_ content是自己有多少占多少。
android:gravity="center"
指定控件中内容的位置,书中是这样介绍的
——第一行代码
我们使用android:gravity来指定文字的对齐方式,可选值有top、bottom. left、right、center等,可以用来同时指定多个值,这里我们指定的center, 效果等同于center_vertical|center_ horizontal, 表示文字在垂直和水平方向都居中对齐。
android:text="This is a text"
android:textColor="#00ff00"
android:textSize="60sp" />
这三个简单明了,分别是文字,文字颜色和文字大小。
这里附上一个文字颜色表,仅供参考
Button
Button按钮的属性基本上和TextView相似,值得注意的是
- 可以使用android:textAllCaps="false"来禁止Button中的文字转化成为大写
- 可以在Activity里面为按钮绑定监听器事件
Button btnId = (Button) findViewById(R.id.btn_id);
btnId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//此处添加逻辑
}
});
EditText
EditText是一个对话框,用处十分广泛,像QQ、短信等等,而控件属性大同小异,而文中提到比较特殊的就是
- android:hint=“xxx” 类似于提示,在登录界面时,每个对话框会提示需要输入的是账号或者密码,当输入文字时消失。
- android:maxLines=“2”。因为之前设置的宽度是可变的,随着输入内容的不断增加会变得不好看,设置了这个最大行数,就可以在超过两行时自动向下拉取进度条,而不调整控件大小。
ImageView
展示图片的控件,通过android:src来指定图片的位置,图片一般存放在drawable中,可以存放不同分辨率的图片,这样在不同设备上都可以显示。
ProgressBar
是界面上显示的一个进度条
为了实现进度条的功能,需要调整控件的可见性。
——第一行代码
Android控件的可见属性。所有的Android控件都具有这个属性,可以通过android:visibility进行指定,可选值有3种: visible、 invisible 和gone。visible 表示控件是可见的,这个值是默认值,不指定android:visibility时,控件都是可见的。invisible 表示控件不可见,但是它仍然占据着原来的位置和大小,可以理解成控件变成透明状态了。gone 则表示控件不仅不可见,而且不再占用任何屏幕空间。我们还可以通过代码来设置控件的可见性,使用的是setVisibility()方法,可以传人View. VISIBLE、View. INVISIBLE和View. GONE这3种值。
其中有一个属性可以设置进度条的格式,就是Style。
<ProgressBar
android:id="@+id/pb_normal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/pb_small"
style="?android:attr/progressBarStyleSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/pb_large"
style="?android:attr/progressBarStyleLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/pb_horizontal"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/pb_horizontal_default"
style="@android:style/Widget.ProgressBar.Horizontal"
android:max="100"
android:progress="50"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
对应
很简单对不对。其中还可以通过android:max="100"
,android:progress="50"
来实现进度条的模拟。
但是这不是一个静止的画面,需要让他动起来。怎么做呢?主要的思路就是,先开一个子线程,在线程中加载耗时操作,并发送信息。然后,通过Handler机制接受信息,更新进度条,完成后隐藏进度条。
第一步:声明变量
private ProgressBar progressBar;
private int mProgress = 0;
private Handler mHandler;
这个不多说,mProgress用来记录进度,默认是零。然后还有一些绑定布局啥的就不说了。
第二步:开辟一个线程
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
这个不解释了吧,实例一个Thread,以匿名内部类的方式传入Runable类,并且重写run方法,然后start()运行run中的内容。
第三步:写一个耗时方法
private int doWork() {
mProgress += Math.random() * 10;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
return mProgress;
}
Math.random()就是生成0到1之间的随机小数,这里+=包括一个强转。之后每次睡2s。
第四步:
while (true) {
mProgress = doWork();
Message m = new Message();
if (mProgress < 100) {
m.what = 0x111;
mHandler.sendMessage(m);
} else {
m.what = 0x110;
mHandler.sendMessage(m);
break;
}
}
首先就是把doWork()的进度传给mProgress,全局变量一般带个m。然后实例一个Message用来传递信息,如果mProgress小于100,m.what为一个值。大于100说明任务完成,传另外一个值,然后别忘记break。
第五步:实例Handler
mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == 0x111) {
progressBar.setProgress(mProgress);
} else {
Toast.makeText(MainActivity.this, "耗时操作已完成", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
}
}
};
实例一个Handler,然后重写handleMessage方法。传入的msg就是之前发送的msg,然后判断一下,更改progress的值,或者完成,隐藏控件。
好了,运行测试,就可以体验动态进度条了。下面附上一个完整的文件,结构更清晰。
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
private int mProgress = 0;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.p);
mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == 0x111) {
progressBar.setProgress(mProgress);
} else {
Toast.makeText(MainActivity.this, "耗时操作已完成", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
mProgress = doWork();
Log.i("TAG", "handleMessage: " + mProgress);
Message m = new Message();
if (mProgress < 100) {
m.what = 0x111;
mHandler.sendMessage(m);
} else {
m.what = 0x110;
mHandler.sendMessage(m);
break;
}
}
}
private int doWork() {
mProgress += Math.random() * 10;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
return mProgress;
}
}).start();
}
}
还有一种Handler传递方式,我觉得更加符合咱们思维。是通过sendEmptyMessage直接传递Message.what,省去了实例化Message的步骤。而接收的时候使用Switch判断。附上实例
public class MainActivity extends AppCompatActivity {
……
@Override
protected void onCreate(Bundle savedInstanceState) {
……
mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 100:
progressBar.setProgress(mProgress);
break;
case 200:
Toast.makeText(MainActivity.this, "耗时操作已完成", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
break;
default:
break;
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
mProgress = doWork();
if (mProgress < 100) {
mHandler.sendEmptyMessage(100);
} else {
m.what = 0x110;
mHandler.sendEmptyMessage(200);
break;
}
}
}
private int doWork() {
……
}
}).start();
}
}
AlertDialog
AlertDialog是一个对话框,在所有控件之上,可以屏蔽其他控件的交互能力。也非常简单。
先展示几个效果
- 普通提示对话框
//双选对话框
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
alertDialog.setIcon(R.mipmap.ic_launcher);
alertDialog.setTitle("乔布斯");
alertDialog.setMessage("活着就是为了改变世界,难道还有还有其他原因吗?");
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "您单击了是按钮", Toast.LENGTH_SHORT).show();
}
});
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "否", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "您单击了否按钮", Toast.LENGTH_SHORT).show();
}
});
alertDialog.show();
具体介绍一下用到的方法吧:
- setTitle:设置对话框的标题,比如“提示”、“警告”等;
- setMessage:设置对话框要传达的具体信息;
- setIcon: 设置对话框的图标;
- setCancelable: 点击对话框以外的区域是否让对话框消失,默认为true;
- setOnShowListener:对话框显示时触发的事件;
- setOnCancelListener:对话框消失时触发的事件。
- setButton:传递了三个参数,第一个就是按钮的类型,BUTTON_NEUTRAL、BUTTON_NEGATIVE和BUTTON_POSITIVE,分别对应的就是图中的对的选择和错的选项。还有个中立选项,位置在弹出的对话框的最左边。第二个参数就是按钮上面显示的文字。第三个参数就是点击事件。
- 列表提示对话框
//带四个列表项的对话框
final String[] items = new String[]{"123", "1233", "456", "789"};
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setIcon(R.mipmap.ic_launcher);
builder.setTitle("请选择你喜欢的标题");
builder.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "您选择了【" + items[which]
+ "】", Toast.LENGTH_SHORT).show();
}
});
builder.create().show();
和普通对话框不一样的地方就是setItems,传入的第一个参数就是需要显示的内容,是一个字符串数组。第二个参数就是点击事件。
这里还有个不一样的地方,就是普通对话是直接操作的AlertDialog对象,而这里是操作了一个AlertDialog内部类Builder对象。两者之间的关系就是Builder对象的create方法可以生成一个AlertDialog对象。
- 单选对话框
final String[] items = new String[]{"1", "2", "3", "4", "5"};
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setIcon(R.mipmap.ic_launcher);
builder.setTitle("请选择你喜欢的标题");
builder.setSingleChoiceItems(items, 0, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "您选择了【" + items[which]
+ "】", Toast.LENGTH_SHORT).show();
}
});
builder.setPositiveButton("确定", null);
builder.create().show();
只是把setItems改成setSingleChoiceItems,同时多了一个参数,就是第二个0,这是默认选择的选择。这里使用
- setPositiveButton:设置正面按钮,表示“积极”、“确认”的意思,第一个参数为按钮上显示的文字,下同;
- setNegativeButton:设置反面按钮,表示“消极”、“否认”、“取消”的意思;
- setNeutralButton:设置中立按钮;
实现了AlertDialog.setButton的方法。
- 多选对话框
final boolean[] checkedItem = new boolean[]{false,true,false,true,false};
final String[] items = new String[]{"1", "2", "3", "4", "5"};
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setIcon(R.mipmap.ic_launcher);
builder.setTitle("请选择你喜欢的标题");
builder.setMultiChoiceItems(items, checkedItem, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
checkedItem[which] = isChecked;
}
});
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String result = "";
for (int i = 0; i < checkedItem.length; i++) {
if (checkedItem[i]) {
result+=items[i]+"";
}
}
if (!"".equals(result)) {
Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
}
}
});
builder.create().show();
之前有Single,自然就有Multi,所以设置函数是setMultiChoiceItems。参数分别是显示文字数组,默认选项数组(boolean数组)和点击事件。
ProgressDialog
ProgressDialog和AlertDialog非常相似,只不过多了一个进度条,感觉更像是安装或者下载时候的提示。用法等于AlertDialog+ProgressBar相似。
——第一行代码
注意,如果在setCancelable()中传人了false, 表示ProgressDialog是不能通过Back键取消掉的,这时你就一定要在代码中做好控制,当数据加载完成后必须要调用ProgressDialog 的dismiss ()方法来关闭对话框,否则ProgressDialog将会一直存在。