第一行代码
继续回顾之前的学习点
第三章 ui设计
ui设计,最关键的一点是通过视觉美感增加用户的粘性,提高一个app的生命周期。
widget常用控件的使用
- TextView
这个文字显示组件已经十分普遍了。详细的内容可以具体参考官方文档。下列的代码实例是copy菜鸟教程中的示例Demo。XML中的语法结构就不赘余了,反正在IDE开发中会进行相应的提示。这其中的属性一般通过翻译也大致比较清晰,若不熟悉可以在文档中进行查看以及在IDE自己敲一敲代码,试一试看看到底是怎么样的效果。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:gravity="center"
android:background="#8fffad">
<TextView
android:id="@+id/txtOne"
android:layout_width="200dp"
android:layout_height="200dp"
android:gravity="center"
android:text="TextView(显示框)"
android:textColor="#EA5246"
android:textStyle="bold|italic"
android:background="#000000"
android:textSize="18sp" />
</RelativeLayout>
- Button
按钮的设计可以很多彩,button对于ui设计来说是中流砥柱。在活动中的跳转也一般通过button来实现的。不过方法有很多,不局限于一种,但我们更乐于选取最优的一个方法。具体的一些实现方法和相应的花哨的用法在菜鸟教程中可以参考。
<Button
android:id="@+id/btnOne"
android:layout_width="match_parent"
android:layout_height="64dp"
android:background="@drawable/btn_bg1"
android:text="按钮"/>
<Button
android:id="@+id/btnTwo"
android:layout_width="match_parent"
android:layout_height="64dp"
android:text="按钮不可用"/>
其中强调一个小点,button默认的字符串都是大写的。 如果要关闭这一个功能需要在xml中添加如下代码: android:textAllCaps="false" 关闭大写。 实现button的点击监听方法主要有实现接口方法和使用内部类以及匿名类。方法的实现看个人喜好。
- EditView
文字编辑和输入都需要这个组件widget。
<EditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="start"
android:background="#ffffff"
android:layout_marginTop="20dp"
android:layout_marginHorizontal="12dp"
android:padding="10dp"
android:hint="请输入你想说的内容"/>
值得注意的一点是hint是提示语句,可以暗示说明用户需要在这里应该输入那些数据信息。 在ui中因为有时候用户输入的数据很多很长导致ui变形;这是可以参考一下代码配置android:maxLines 进行限制。这样就不会使得整个ui变得畸形。
- ImageView
图片,这一个对于所有的用户体验来说是最可观的,也是最直接的widget组件。
在API文档中我们发现ImageView有两个可以设置图片的属性,分别是:src和background
①background通常指的都是背景,而src指的是内容!! ②当使用src填入图片时,是按照图片大小直接填充,并不会进行拉伸 而使用background填入图片,则是会根据ImageView给定的宽度来进行拉伸
<ImageView
android:layout_width="200dp"
android:layout_height="wrap_content"
android:background="@drawable/pen" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pen" />
一般图片资源都会放在drawable下。
- ProgressBar
进度条,这里跟用户交互一般是在一些比较耗时的场景下使用。这里会给用户一种比较良好的交互感。
<ProgressBar
style="@android:style/Widget.ProgressBar.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
这一部分的操作在xml中比较少,更多的是结合实际的业务代码逻辑在逻辑中进行设计和操作。
-
AlertDialog 对话框这一部分主要是避免用户的一些误删操作等等。增强这个app的可控性。这一部分主要还是在逻辑代码中实现。当然你也可以自己设计属于自己风格的一些样式,最后在组装起来。
//普通对话框
case R.id.btn_dialog_one:
alert = null;
builder = new AlertDialog.Builder(mContext);
alert = builder.setIcon(R.mipmap.ic_icon_fish)
.setTitle("系统提示:")
.setMessage("这是一个最普通的AlertDialog,\n带有三个按钮,分别是取消,中立和确定")
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(mContext, "你点击了取消按钮~", Toast.LENGTH_SHORT).show();
}
})
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(mContext, "你点击了确定按钮~", Toast.LENGTH_SHORT).show();
}
})
.setNeutralButton("中立", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(mContext, "你点击了中立按钮~", Toast.LENGTH_SHORT).show();
}
}).create(); //创建AlertDialog对象
alert.show(); //显示对话框
break;
这个alertDialog主要就是一个builder重要点。不要忘记show出来!!!跟Toast一样,要是不show出来写了也没有,用户看不到。
- ProgressDialog
进度条对话框,这个和AlertDialog其实功能上差不多,只是多了一个progressBar进行动态的交互设计。这一点也是一般用在高耗时的工作上。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button btn_one;
private Button btn_two;
private Button btn_three;
private ProgressDialog pd1 = null;
private ProgressDialog pd2 = null;
private final static int MAXVALUE = 100;
private int progressStart = 0;
private int add = 0;
private Context mContext = null;
//定义一个用于更新进度的Handler,因为只能由主线程更新界面,所以要用Handler传递信息
final Handler hand = new Handler()
{
@Override
public void handleMessage(Message msg) {
//这里的话如果接受到信息码是123
if(msg.what == 123)
{
//设置进度条的当前值
pd2.setProgress(progressStart);
}
//如果当前大于或等于进度条的最大值,调用dismiss()方法关闭对话框
if(progressStart >= MAXVALUE)
{
pd2.dismiss();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = MainActivity.this;
bindViews();
}
private void bindViews() {
btn_one = (Button) findViewById(R.id.btn_one);
btn_two = (Button) findViewById(R.id.btn_two);
btn_three = (Button) findViewById(R.id.btn_three);
btn_one.setOnClickListener(this);
btn_two.setOnClickListener(this);
btn_three.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_one:
//这里的话参数依次为,上下文,标题,内容,是否显示进度,是否可以用取消按钮关闭
ProgressDialog.show(MainActivity.this, "资源加载中", "资源加载中,请稍后...",false,true);
break;
case R.id.btn_two:
pd1 = new ProgressDialog(mContext);
//依次设置标题,内容,是否用取消按钮关闭,是否显示进度
pd1.setTitle("软件更新中");
pd1.setMessage("软件正在更新中,请稍后...");
pd1.setCancelable(true);
//这里是设置进度条的风格,HORIZONTAL是水平进度条,SPINNER是圆形进度条
pd1.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd1.setIndeterminate(true);
//调用show()方法将ProgressDialog显示出来
pd1.show();
break;
case R.id.btn_three:
//初始化属性
progressStart = 0;
add = 0;
//依次设置一些属性
pd2 = new ProgressDialog(MainActivity.this);
pd2.setMax(MAXVALUE);
pd2.setTitle("文件读取中");
pd2.setMessage("文件加载中,请稍后...");
//这里设置为不可以通过按取消按钮关闭进度条
pd2.setCancelable(false);
pd2.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
//这里设置的是是否显示进度,设为false才是显示的哦!
pd2.setIndeterminate(false);
pd2.show();
//这里的话新建一个线程,重写run()方法,
new Thread()
{
public void run()
{
while(progressStart < MAXVALUE)
{
//这里的算法是决定进度条变化的,可以按需要写
progressStart = 2 * usetime() ;
//把信息码发送给handle让更新界面
hand.sendEmptyMessage(123);
}
}
}.start();
break;
}
}
//这里设置一个耗时的方法:
private int usetime() {
add++;
try{
Thread.sleep(100);
}catch (InterruptedException e) {
e.printStackTrace();
}
return add;
}
}
以上的代码示例基本的样式已经展现,耗时动作还是根据业务需求进行哦~
布局使用
组件需要整合起来,就得需要整体嵌套起来。毕竟“团结就是力量”。
- LinearLayout
线性布局,这里最重要的是垂直线性还是水平线性;示例代码如下:
<LinearLayout
android:id="@+id/ll_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"//重要!!!!!
android:visibility="gone" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:color/holo_purple"
android:gravity="center_horizontal"
android:padding="10dp"
android:text="水平布局" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:color/holo_green_light"
android:gravity="center_horizontal"
android:padding="10dp"
android:singleLine="true"
android:text="水平布局" />
</LinearLayout>
</LinearLayout>
在线性布局中有权重layout_weight 这个关键的配置。 在线性布局还有一个重要的属性android:layout_weight,这个属性允许我们使用比例的方式来指定控件的大小
例如:在这个的权重比下编辑框和按钮一样宽。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<EditText
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="编辑框"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="1"/>
</LinearLayout>
- RelativeLayout
相对布局,这个相对可以对于父布局而言也可以对各个小组件来说。 先post相对父类来说的代码示例; 一般来说alignParentXXX 就是对应父组件来说的,这里的XXX指的是方位例如right ,left 等等。
<RelativeLayout
android:id="@+id/rl_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/tittle_bg"
android:padding="10dp"
android:visibility="gone" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:drawableLeft="@drawable/arrow_pressed"
android:drawablePadding="5dp"
android:text="动态" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="好友动态" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/tittle_add" />
</RelativeLayout>
相对其他组件而言:layout_toXXXof 是关键,同理这里XXX是方位,如right 和left 等等
<Button
android:id="@+id/b3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="3"/>
<Button
android:id="@+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/b3"
android:layout_toLeftOf="@+id/b3"//示例
android:text="1"/>
<Button
android:id="@+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/b3"
android:layout_toRightOf="@+id/b3"
android:text="2"/>
在这里要重点区分两个概念:margin针对的是容器中的组件,而padding针对的是组件中的元素,要区分开来!
首先margin代表的是偏移,比如marginleft = "5dp"表示组件离容器左边缘偏移5dp; 而padding代表的则是填充,而填充的对象针对的是组件中的元素,比如TextView中的文字 比如为TextView设置paddingleft = “5dp”,则是在组件里的元素的左边填充5dp的空间!
-
FrameLayout 帧布局,这里可以参考菜鸟教程的学习,重点是要知道这个布局的关键在于“帧”。还记得小时候玩的flash动画嘛?就是一帧一帧的。这个布局就体现了这样的“帧”特性。具有覆盖性!根据这样的特性,我特地根据菜鸟里的资源自己构建一个动漫小女孩奔跑Demo。 这一块就不附上代码了,详细的参考Demo代码。 -
百分比布局 PercentRelativeLayout… 百分比布局的出现是为了高效地满足控制组件大小的目的。不过使用的是百分比%进行控制,layout_heightPercent 等等?例如:
<?xml version="1.0" encoding="utf-8"?>
<androidx.percentlayout.widget.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView1"
android:layout_alignParentBottom="true"
android:gravity="center"
android:textSize="20sp"
android:background="#FF1493"
android:textColor="#ffffff"
app:layout_widthPercent = "15%"
app:layout_heightPercent = "100%"
app:layout_marginPercent="4%"
android:text="100%"
/>
<TextView
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView2"
android:layout_toRightOf="@+id/textView1"
android:gravity="center"
android:textSize="20sp"
android:background="#FF4081"
android:textColor="#ffffff"
app:layout_widthPercent = "15%"
app:layout_heightPercent = "80%"
app:layout_marginPercent="4%"
android:text="80%"/>
<TextView
android:id="@+id/textView3"
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/textView2"
android:gravity="center"
android:textSize="20sp"
android:background="#FF6EB4"
android:textColor="#ffffff"
app:layout_widthPercent = "15%"
app:layout_heightPercent = "60%"
app:layout_marginPercent="4%"
android:text="60%"/>
<TextView
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView4"
android:layout_toRightOf="@+id/textView3"
android:gravity="center"
android:textSize="20sp"
android:background=" #FFB5C5"
android:textColor="#ffffff"
app:layout_widthPercent = "15%"
app:layout_heightPercent = "40%"
app:layout_marginPercent="4%"
android:text="40%"/>
</androidx.percentlayout.widget.PercentRelativeLayout>
自定义控件
在进行自定义控件之前,首先要充分认识布局。 根据文档的定义:
布局定义了应用中的界面结构(例如 Activity 的界面结构)。布局中的所有元素均使用 View 和 ViewGroup 对象的层次结构进行构建。View 通常用于绘制用户可看到并与之交互的内容。ViewGroup 则是不可见的容器,用于定义 View 和其他 ViewGroup 对象的布局结构。
- 布局引入
关键的xml语句是<include layout =''> 这里可以参考我自己写的一个Demo,主要就是更换一个标题栏。
//隐藏原标题栏,这段代码是在mainActivity中展现
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
actionBar.hide();
}
通过继承实现复用,多态
public class TitleLayout extends LinearLayout {
public TitleLayout(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.title,this);
}
}
但是以上这个代码会导致报错,没办法加载出正确的TitleLayout。修改代码如下;
public class TitleLayout extends LinearLayout {
public TitleLayout(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.title,this);
}
//这个构造方法才是最重要的!!!之前少了一个参数就出错了,至于这个的原因,之后看看源码好好了解一下
public TitleLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.title,this);
}
}
ListView
这个虽然已经不如RecyclerView主流,但是还是要熟悉一下。明白来路更好地向前。在《第一行代码》中直接给的标题就是最常用和最难用的控件 ,虽然现在回过头看有点言过其实,但是那个时代的确如此!附上主要的逻辑代码:
public class MainActivity extends AppCompatActivity {
ListView listView;
ArrayList<String> data = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createData();
listView = findViewById(R.id.listView);
//重点是使用适配器进行适配的
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,data);
listView.setAdapter(arrayAdapter);
}
private void createData() {
for(int i = 0; i < 100; i++){
data.add(String.valueOf("num is "+i));
}
}
}
回顾上面的知识点,你也可以自己构建一个item的布局。具体的实现逻辑可以参考我自建的简单Demo。
当熟悉一个新的组件的时候后,需要考虑如何提高其性能:通过缓存在实际中可以提高运行效率。不过现在的cpu已经很强大,这些性能问题也许就不像之前那么必不可少。主要是实现功能。以下的代码中view.getTag 和view.setTag 这两个需要回顾一下view的方法文档。本段代码可以实现高效运行ListView。
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Animal animal = (Animal) getItem(position);
ViewHolder viewHolder = new ViewHolder();
View view;
if(convertView == null){
view = LayoutInflater.from(getContext()).inflate(animalResourceId,parent,false);//这个代码很常用,需要认真看看源码,最后的boolean有什么用。
viewHolder.imageView = view.findViewById(R.id.imageView);
viewHolder.textView = view.findViewById(R.id.textView);
view.setTag(viewHolder);//view中储存该控件的组件
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();//获取控件
}
viewHolder.imageView.setImageResource(animal.getPicID());
viewHolder.textView.setText(animal.getName());
return view;
}
//使用一个内部类,这样里面的组件直接就示例
class ViewHolder{
ImageView imageView ;
TextView textView ;
}
item搞定,那么也得设计一些监听事件,让整个listview活过来。 AdapterView.OnItemClickListener 是监听方法。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getApplicationContext(),"position:"+position,Toast.LENGTH_SHORT).show();
}
});
RecyclerView
recyclerView几乎具有List View的全部“遗产”,比ListView更高级!Google的官方文档都推荐使用Recycler View,得到“爸爸”的鼎力支持还能不用吗?
确定布局后,您需要实现 Adapter 和 ViewHolder。这两个类配合使用,共同定义数据的显示方式。ViewHolder 是包含列表中各列表项的布局的 View 的封装容器。Adapter 会根据需要创建 ViewHolder 对象,还会为这些视图设置数据。将视图与其数据相关联的过程称为“绑定”。
布局的样式可以:
RecyclerView 中的列表项由 LayoutManager 类负责排列。RecyclerView 库提供了三种布局管理器,用于处理最常见的布局情况:
LinearLayoutManager 将各个项排列在一维列表中。 GridLayoutManager 将所有项排列在二维网格中:
如果网格垂直排列,GridLayoutManager 会尽量使每行中所有元素的宽度和高度相同,但不同的行可以有不同的高度。 如果网格水平排列,GridLayoutManager 会尽量使每列中所有元素的宽度和高度相同,但不同的列可以有不同的宽度。
StaggeredGridLayoutManager 与 GridLayoutManager 类似,但不要求同一行中的列表项具有相同的高度(垂直网格有此要求)或同一列中的列表项具有相同的宽度(水平网格有此要求)。其结果是,同一行或同一列中的列表项可能会错落不齐。 核心的代码如下:详细的内容可以参考Demo,注意该Demo中是含有ListView和RecyclerView。
AnimalRecyclerAdapter animalAdapter = new AnimalRecyclerAdapter(animals);//animals是自己配的数据,根据业务需求自己配
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,4);
recyclerView.setAdapter(animalAdapter);
recyclerView.setLayoutManager(gridLayoutManager);
点九图 nine patch
何为点九图?
点九图又称九图,是一种png格式的图片,其后缀为.9.png,其与传统png图片不同的地方是,点九图的四周边缘各>有1个像素宽高的区域,而且只能填充两种颜色,透明(#00000000)和黑色(#FF000000),其目的是用于对该图片的扩展区域和内容显示区域进行定义。使用点九PNG技术,可以将图片横向和纵向同时进行拉伸,并能保持图片细节,不会因为图片拉伸而模糊失真。
了解之后,可以参考我自己写的Demo进行修改。该Demo是最常用的仿微信的聊天框ui设计。其中的代码可以再自己update一下。
|