UI开发的点点滴滴

如何编写程序界面

Android中有多种编写程序界面的方式可供选择。Android Studio和 Eclipse总都提供了相应的可视化编辑器,允许使用拖放控件的方式来编写布局,并能在试图上直接修改控件的属性

缺点:可视化编辑工具不利于真正了解界面背后的实现原理,通过这种方式制作出的界面通常不具有很好的屏幕适配性,而且当需要编写较复杂的界面时,可视化编辑工具将很难胜任

常见控件的使用方法

TextView

match_parent:让当前控件的大小和福布局的大小一样,也就是由父布局界定当前控件的大小

wrap_content:让当前控件的大小能够刚好包含住里面的内容,也就是由控件内容决定当前控件的大小

android:gravity:可选值有top、bottom、left、right、center等,可以用”|”来同时制定多个值,制定center效果等同于center_vertical|center_horizontal,表示文字在垂直和水平方向都居中

android中字体大小可以使用sp作为单位

Button

android:textAllCaps=”false” 禁用大小写转换

EditText

android:hint属性指定了一段提示性的文本

android:maxLines=”2”指定了EditText的最大行数为两行,这样当输入的内容超过两行时,文本就会向上滚动,而EditText则不会再继续拉伸

ProgressBar

style="?anddroid:attr/progressBarStyleHorizontal"
style="@anddroid:style/Widget.progressBar.Horizontal"

使用@表示侍弄固定的style,而不会跟随Theme改变,这style可以在对应的style.xml中找到。而?表示从theme中查找引用的资源名

visable表示控件是可见的,这个值是默认的

不指定android:visiblity时,控件都是可见的。invisable表示控件不可见,但是它仍然占据着原来的位置和大小,可以理解成控件变成透明状态了。gone则表示控件不仅不可见,而且不再战虎任何屏幕空间

我们还可以通过代码来设置控件的可见性,使用的是setVisibility()方法,可以传入View.VISIBLE、View.INVISIBLE和View.GONE 这3种值

通过android:max属性给进度条设置一个最大值,然后在代码中动态地更改进度条的进度

AlertDialog

AlertDialog可以在当前的界面弹出一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此AlertDialog一般都是用于提示一些非常重要的内容或者警告信息。比如为了防止用户误删重要内容,在删除前弹出一个确认对话框

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button button = (Button) findViewById(R.id.button);button.setOnClickListener(this);
}@Override
public void onClick(View view) {switch (view.getId()) {case R.id.button:AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);dialog.setTitle("This is Dialog");dialog.setMessage("Something important.");dialog.setCancelable(false);//确认dialog.setPositiveButton("ok", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {}});//取消dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {}});dialog.show();break;default:break;}
}

ProgressDialog

ProgressDialog和AlertDialog优点类似,都可以在界面上弹出一个对话框,都能够屏蔽掉其他空间的交互能力。不同的是,ProgressDialog会在对话框中显示一个进度条,一般用于表示当前操作比较耗时,让用户耐心等待

@Override
public void onClick(View view) {switch (view.getId()) {case R.id.button:ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);progressDialog.setTitle("This is ProgressDialog");progressDialog.setMessage("loading...");progressDialog.setCancelable(true);progressDialog.show();break;default:break;}
}

先构建出一个ProgressDialog对象,然后同样可以设置标题、内容、可否取消等属性,最后也是通过show()方法将ProgressDialog显示出来

注意:如果在setCancelable()中传入了false,表示ProgressDialog是不能通过back键取消的,当数据加载完成后必须要调用ProgressDialog的dismiss()方法来关闭对话框,否则ProgressDialog将会一直存在

详解四种基本布局

线性布局

将其所包含的控件在现行方向上依次排列

LinearLayout的排列方向是horizontal,内部的控件就绝对不能将宽度指定为match_parent,因为这样的话,单独的一个控件会将整个水平方向占满,其他的空间就没有可放置的位置了

android:layout_gravity用于指定控件在布局中国的对齐方式

LinearLayout的排列方向是horizontal时,只有垂直方向上的对齐方式才会生效,因为此时水平方向上的长度是不固定的,每添加一个控件,水平方向上的长度都会改变因而无法指定该方向上的对其方式;同样当LinearLayout的排列方向是vertical时只有水平方向上的对其方式才会生效 
android:layout_weight属性允许我们使用比例的方式来指定控件的大小,它在手机屏幕的适配性方面可以起到非常重要的作用

dp是android中用于指定控件大小、属性的单位

系统会把LinearLayout下所有控件指定的layout_weight值相加,得到一个总值,然后每个控件所占大小的比例就是该控件的layout_weight值除以刚才算出的总和

相对布局

RelativeLayout又称作相对布局,也是一种非常常用的布局

android:layout_above属性可以让一个控件位于另一个控件的上方 
layout_alignLeft表示让控件的左边缘和另一个控件的左边缘对齐

帧布局

FrameLayout又称作帧布局,这种布局没有方便的定位方式,所有的控件都会默认八方在布局的左上角

百分比布局

提供了PercentFrameLayout和PercentRelativelayout这两个全新的布局

android团队将百分比布局定义在support库当中,我们只需要在项目的build.gradle中添加百分比布局库的依赖,就能保证百分比布局在android所有系统版本上的兼容性了

PercentFrameLayout还是会继承FrameLayout的特性,即所有的控件默认都是摆放在布局的左上角

创建自定义控件

所有控件都是直接或间接继承自view的,所欧阳的所有布局都是直接或间接继承自ViewGroup的。View是Android中最基本的一种UI 组件,它可以在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件,因此,我们使用的各种控件其实就是在View的基础之上又添加了各自特有的功能。而ViewGroup则是一种特殊的View,它可以包含很多子View和子ViewGroup,是一个用于放置控件和布局的容器

引入布局

创建title.xml

<?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="wrap_content"
android:background="@drawable/title_bg"><Buttonandroid: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"/><TextViewandroid: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"/><Buttonandroid: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中

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.demo.uicustomviews.MainActivity"><include layout="@layout/title"/></LinearLayout>

在MainActivity中将系统自带的标题栏隐藏掉 
public class MainActivity extends AppCompatActivity {

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//隐藏系统自带的标题栏ActionBar actionBar = getSupportActionBar();if (actionBar != null) {actionBar.hide();}}
}

创建自定义控件

新建TitleLayout继承自LinearLayout,让它成为我们自定义的标题栏控件

public class TitleLayout extends LinearLayout {public TitleLayout(Context context, AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.title , this);}
}

通过LayoutInflater的from()方法可以构建一个LayoutInflater对象,然后调用inflate()方法就可以动态加载一个布局文件,inflate()方法接收两个参数,第一个参数是要加载的布局文件的id,这里我们传入R.layout.title,第二个参数是给加载好的布局再添加一个父布局,这里我们想要指定为TitleLayout,于是直接传入this

在布局文件中添加这个自定义控件,修改activity_main.xml中的代码,如下:

 <com.demo.uicustomviews.TitleLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"/>

在标题栏中的按钮注册点击事件,修改TitleLayout中的代码:

public class TitleLayout extends 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() {@Overridepublic void onClick(View view) {((Activity) getContext()).finish();}});titleEdit.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View view) {Toast.makeText(getContext() , "You clicked Edit button" , Toast.LENGTH_SHORT).show();}});}
}

RecyclerView

可以轻松实现了和listView同样的效果,还优化了ListView中存在的各种不足之处

为RecyclerView准备一个适配器,新建一个FruitAdapter类,让这个适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder。

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{private List<Fruit> mFruitList;static class ViewHolder extends RecyclerView.ViewHolder {ImageView fruitImage;TextView fruitName;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;}@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(ViewHolder holder, int position) {Fruit fruit = mFruitList.get(position);holder.fruitImage.setImageResource(fruit.getImageId());holder.fruitName.setText(fruit.getName());}@Overridepublic int getItemCount() {return mFruitList.size();}}

首先定义了一个内部类ViewHolder,ViewHolder要继承自RecyclerView.ViewHolder。然后ViewHolder的构造函数中要传入一个view参数,这个参数通常就是RecyclerView子项的最外层布局,那么我们就可以通过findViewById()来获取布局中的 ImageView和TextView的实例了

public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initFruits();RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);LinearLayoutManager layoutManager = new LinearLayoutManager(this);recyclerView.setLayoutManager(layoutManager);FruitAdapter adapter = new FruitAdapter(fruitList);recyclerView.setAdapter(adapter);}/***  初始化水果数据*/private void initFruits() {for (int i = 0 ; i < 2 ; i ++) {Fruit apple = new Fruit("Apple" , R.drawable.apple_pic);fruitList.add(apple);Fruit banana = new Fruit("Banana" , R.drawable.banana_pic);fruitList.add(banana);Fruit orange = new Fruit("Orange" , R.drawable.orange_pic);fruitList.add(orange);Fruit watermelon = new Fruit("Watermelon" , R.drawable.watermelon_pic);fruitList.add(watermelon);Fruit pear = new Fruit("Pear" , R.drawable.pear_pic);fruitList.add(pear);Fruit grape = new Fruit("Grape" , R.drawable.grape_pic);fruitList.add(grape);Fruit pineapple = new Fruit("Pineapple" , R.drawable.pineapple_pic);fruitList.add(pineapple);Fruit strawberry = new Fruit("Strawberry" , R.drawable.strawberry_pic);fruitList.add(strawberry);Fruit cherry = new Fruit("Cherry" , R.drawable.cherry_pic);fruitList.add(cherry);Fruit mango = new Fruit("Mango" , R.drawable.mango_pic);fruitList.add(mango);}}
}

首先初始化所有的水果数据,接着在onCreate()方法中我们先获取到RecyclerView的实例,然后创建一个LinearLayoutManager对象,并将它设置到RecyclerView当中。layoutManager用于指定recyclerView的布局方式,这里使用LinearLayoutManager时线性布局的意思,可以实现和listView类似的效果。是接下来创建FruitAdapter的实例,并将水果数据传入到FruitAdapter的构造函数中,最后调用RecyclerView的setAdapter()方法来完成适配器设置,这样RecyclerView和数据支架的关联就建立完成了

实现横向滚动和瀑布流布局

修改fruit_item.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="100dp"android:layout_height="wrap_content"android:orientation="vertical"><ImageViewandroid:id="@+id/fruit_image"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal" /><TextViewandroid:id="@+id/fruit_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginTop="10dp" /></LinearLayout>

在MainActivity中加入一行代码 
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); 
用LinearLayoutManager的setOrientation()方法来设置布局的排列方向,默认是纵向排列的,传入LinearLayoutManager.HORIZONTAL表示让布局横向排列

ListView和RecyclerView的区别: 
ListView的布局排列是由自身去管理的,而RecyclerView则将这个工作交给 LayoutManager ,LayoutManager中指定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各种不同排列方式的布局

除此之外,RecyclerView还给我们提供了GridLayoutManager和StaggredGridLayoutManager这两种内置的布局方式。GridLayoutManager可以用于实现网格布局,StaggredGridLayoutManager可以用于实现瀑布流布局

public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initFruits();RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);recyclerView.setLayoutManager(layoutManager);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);}}private String getRandomLengthName(String name) {Random random = new Random();int length = random.nextInt(20) + 1;StringBuilder builder = new StringBuilder();for (int i = 0 ; i < length ; i ++) {builder.append(name);}return builder.toString();}}

RecyclerView的点击事件

修改FruitAdapter中的代码

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{private List<Fruit> mFruitList;static class ViewHolder extends RecyclerView.ViewHolder {View 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;}@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 view) {int position = holder.getAdapterPosition();Fruit fruit = mFruitList.get(position);Toast.makeText(view.getContext() , "you clicked view" + fruit.getName() , Toast.LENGTH_SHORT).show();}});//实现子项中ImageView的点击事件holder.fruitImage.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {int position = holder.getAdapterPosition();Fruit fruit = mFruitList.get(position);Toast.makeText(view.getContext() , "you clicked image" + fruit.getName() , Toast.LENGTH_SHORT).show();}});return holder;}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {Fruit fruit = mFruitList.get(position);holder.fruitImage.setImageResource(fruit.getImageId());holder.fruitName.setText(fruit.getName());}@Overridepublic int getItemCount() {return mFruitList.size();}}

第二行代码第三章笔记相关推荐

  1. 第一行代码第三版笔记

    第3章 Activity 主acitivity:程序运行起来首先启动的activity manifest <?xml version="1.0" encoding=" ...

  2. Android第一行代码——第三章:控件的使用方法5A-00-1A-C4-8C-55(2009+jjy)

    标题:这章主要学习如何控件的使用方法 1 TextView. 创建activity_main.xml中的代码 代码如下: <?xml version="1.0" encodi ...

  3. 《第二行代码》学习笔记

    楼主自序:学生党一枚,没啥上进心,学习不努力所以来到西南某三 线城市一所二流高校,(悔恨呀,当初学习为啥不努力),大二转专业后才开始慢慢的接触,IT这一行,(以前最多就会用电脑玩纸牌接龙),转专业之后 ...

  4. 第一行代码——第十三章:继续进阶——你还应该掌握的高级技巧

    目录: 13.1 全局获取 Context的技巧 13.2 使用 Intent传递对象 13.2.1 Serializable 方式 13.2.2 Parcelable 方式 13.3 定制自己的日志 ...

  5. 《第二行代码》—— 酷欧天气的开发

    使用的教材是国@郭霖写的<第二行代码> 应用名:Cool Weather 一.功能需求及技术可行性分析 1.具备的功能 可以罗列出全国所有的省市县 可以查看全国任意城市的天气信息 可以自由 ...

  6. 软考中级-网络工程师第三章笔记(广域通信网)

    软考中级-网络工程师第三章笔记(广域通信网) 文章目录 软考中级-网络工程师第三章笔记(广域通信网) 前言 一.广域网概念和分类 二.公共交换电话网PSTN 三.公共数据网X.25 四.帧中继网FR ...

  7. hashMap put方法 第二行代码

    if (table == EMPTY_TABLE) {inflateTable(threshold); } table transient Entry<K,V>[] table = (En ...

  8. 第一行代码-android-第三版-pdf扫描-思维导图-课件-源码

    第一行代码-android-第三版-pdf扫描-思维导图-课件-源码 一帮公众号各种要你关注, 各种压缩包层层套娃要密码, 还要进群, 真他妈日了gou了,找了半天 分享给大家, 毫无套路! pdf扫 ...

  9. (矩阵分析基础(第二版)第三章 矩阵的分解 3.3埃尔米特(hermite)矩阵及其分解)

    (矩阵分析基础(第二版)第三章 矩阵的分解 3.3埃尔米特(hermite)矩阵及其分解) 文章目录 (矩阵分析基础(第二版)第三章 矩阵的分解 3.3埃尔米特(hermite)矩阵及其分解) 1.埃 ...

最新文章

  1. 一文详解随机一致性采样算法:RANSAC
  2. 深入理解Java 8 Lambda表达式(Oracle官方文档版)
  3. Eclipse将android 类编译为jar类库
  4. clearcase 创建副本
  5. linux 查看nexus状态,在linux上搭建nexus私服(CentOS7)
  6. jboss与nginx_JBoss BRMS与JasperReports进行报告
  7. mysql win linux性能对比,不同系统上 MySQL 的性能对比
  8. 子进程和父进程的结论_Python的多进程不是随便用滴!
  9. 1. JDK版本变迁和各版本新特性
  10. 训练集、测试集、验证集
  11. 吴国平:开发旅游小镇是一款养成类游戏 | 十年二十人
  12. oracle18c静默安装教程,CentOS7无图形化界面静默安装oracle18c
  13. 现在才知道,菊花茶可不能随便喝!
  14. python大游戏_Python开发【项目】:大型模拟战争游戏(外星人入侵)
  15. 群晖nas存储系统原理_今夜来谈群晖----缓存、NAS和SSD那些事
  16. [ Linux ] 可重入函数,volatile 关键字,SIGCHLD信号
  17. #力扣 LeetCode35. 搜索插入位置 #在所有 Java 提交中击败了 100.00% 的用户 @FDDLC
  18. go语言下载gin失败解决方案
  19. vue中父组件传图片路径src给子组件无法正常显示图片
  20. 图解项目产品需求评审流程及详细的评审规范实例

热门文章

  1. 雷军:努力工作,克制贪婪,是世界上最笨也最高明的办法
  2. html5创建自定义标签,在html中创建自定义标签
  3. 三 OpenGL常见平面图形绘制代码
  4. python基于Python的资产管理系统毕业设计-附源码201117
  5. android5.1平板或手机应用层一些常用的修改项(eg:MTK8163)
  6. # 震惊!软件测试原来是这么回事?!——bilibili
  7. Linkedin怎么导出好友的邮箱、电话、社交账号?
  8. hbase 2.4 java.lang.NoSuchMethodError: java.nio.ByteBuffer.rewind()Ljava/nio/ByteBuffer
  9. AI测试自动化脚本:解析结果txt文件字段并保存至excel文件
  10. 机器学习分类算法之LightGBM(梯度提升框架)