• 资料来源如下
  • 第一行代码(第二版)
  • ListView
  • Android ListView工作原理完全解析,带你从源码的角度彻底理解

  • ListView是所有原生控件中使用频率最高和最复杂的,涉及知识点也较多,专门抽出一篇来记录,涉及一些过程分析,希望自己能写完

ListView的简单使用

  • 先来看一下一个简单的实例
    代码如下

    • 在activity_main.xml中添加_ListView
      1
      2
      3
      4
      5
      <ListView
      android:id="@+id/list_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
      </ListView>
    • 修改MainActivity的代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
        //要显示的数组,屏幕长了点,为了显示滑动效果多增加了几项。
      private String[] data = {"apple","banana","orange","watermelon","pear","grape","pineapple","strawberry","cherry","mango","A","B","C","D","E","F"};

      @Override
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
      setSupportActionBar(toolbar);

      //为适配器绑定数据
      ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_single_choice,data);
      //得到ListView对象的引用
      ListView listView = (ListView) findViewById(R.id.list_view);
      //适配器内容传递
      listView.setAdapter(adapter);
      });
      }
  • 效果如图
    Screenshot_20160812-170816.png

  • 步骤其实比较简单

    • 准备要显示的数据 ——String[] data数组
    • 构建适配器 ——ArrayAdapteradapter
    • 将适配器添加到ListView ——listView.setAdapter(adapter);
  • 详解

    • ListView首先是用来展现大量数据的,
      数据源的来源可以是网络下载,程序内置、数据库提取等,本例中则是简单的定义了一个String类型 data数组。
      数据源的类型/种类繁多,使得ListView无法直接适配数据源,直接适配数据源将导致代码的臃肿和效率的低下。ListView与数据源之间需要一个过度
    • 适配器:Adapter,Adapter在ListView和数据源之间起到了一个桥梁的作用。正是Adapter的使用ListView的使用变得要比其它控件复杂得多。

Adapter适配器

  • Adapter适配器在ListView和数据源之间起到了一个桥梁的作用Adapter的接口都是统一的,因此ListView不用担心任何适配方面的问题。

    Adapter又是一个接口(interface),它可以去实现各种各样的子类,每个子类都能通过自己的逻辑来去完成特定的功能,以及与特定数据源的适配操作,比如说ArrayAdapter可以用于数组和List类型的数据源适配,SimpleCursorAdapter可以用于游标类型的数据源适配

    这样就解决了数据源适配的难题,并且还拥有相当不错的扩展性

  • 简易的示图(来自 郭霖的博客)
    SouthEast.png

  • 常用Adapter

    ArrayAdapter——用来绑定一个数组,支持泛型操作

    SimpleAdapter——用来绑定在xml中定义的控件对应的数据

    SimpleCursorAdapter——用来绑定游标得到的数据

    BaseAdapter——通用的基础适配器

  • 实例中使用的是ArrayAdapter,ArrayAdapter具有多个构造函数重载,这里使用的是字符串类型,

  • ArrayAdapter的构造函数

    • 当前上下文
    • ListView子项的id,本例中使用的是android.R.layout.simple_list_item 这是Android内置的一个布局文件
    • 需要适配的数据
      之后适配器就构建完成,最后传递进LIstView中即可

自定义适配器

  • 从第一行代码第二版的源码LiseViewTest目录下拷贝drawble_hdpi _到你的工程下

  • 新建Fruit类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Fruit {

    private String name;
    private int imageId;
    //构造函数 水果名称/水果对应图片资源ID
    public Fruit(String name, int imageId) {
    this.name = name;
    this.imageId = imageId;
    }

    public String getName() {
    return name;
    }

    public int getImageId() {
    return imageId;
    }
    }
  • 在layout目录下新建fruit_item.xml _

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <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>

    一个ImageView和TextView ,水平居中显示

  • 自定义适配器继承自ArrayAdapter 泛型指定为fruit类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     //继承自ArrayAdapter
    public class FruitAdapter extends ArrayAdapter<Fruit> {
    private int resourceId;
    //构造函数 /上下文 ListView子项布局id 数据
    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);
    TextView fruitName = (TextView)view.findViewById(R.id.fruit_name);
    fruitImage.setImageResource(fruit.getImageId());
    fruitName.setText(fruit.getName());
    return view;
    }
    }
    • getView()方法接受的三个参数,第一个参数position代表当前子元素的的位置,我们可以通过具体的位置来获取与其相关的数据。第二个参数convertView
    • LayoutInflater.inflate()方法来去加载布局。接收3个参数,第三个参数为false表示只在父布局声明的layout属性有效,但不会为这个view添加父布局,最后设定为接下来会对这个view进行一些属性和值的设定,最后将view返回。
  • 修改MainActivity的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    public class MainActivity extends AppCompatActivity {

    //新建Fruit类的数组
    private List<Fruit> fruitList = new ArrayList<Fruit>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // 初始化水果数据
    initFruits();
    //设置FruitAdapter适配器
    FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
    //得到ListView的引用
    ListView listView = (ListView) findViewById(R.id.list_view);
    listView.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);
    }
    }
    }
  • 只需要修改fruit_item.xml,即可定制界面_

提升ListView的效率

convertView参数

  • 针对 Fruit fruit = getItem(position); // 获取当前项的Fruit实例
    每次滑动ListView时,getView()方法都会将布局都会重新加载一边

  • convertView参数:将之前加载完成的布局进行缓存。

  • 借助convertView参数,在每次加载View之前查询convertView是否为空,为空则重新加载,不为空则从convertView中取

  • 代码部分如下

    1
    2
    3
    4
    5
    6
    7
    8
    View view;
    Fruit fruit = getItem(position); // 获取当前项的Fruit实例

    if (convertView == null) {
    view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
    }else {
    view = convertView;
    }
  • ListView不会再重复加载布局了

    ViewHolder

  • ViewHolder不是Android的开发API,而是一种设计方法

  • 针对
    ImageView fruitImage = (ImageView)view.findViewById(R.id.fruit_image); TextView fruitName = (TextView)view.findViewById(R.id.fruit_name);
    每次布局实例化后,findViewById都会重新执行获取控件实例

  • 新增内部类 ViewHolder,利用Tag附加到对应View中,即每次加载布局时,对应的 获取控件实例的操作也一并执行,并储存在View中。这样 获取控件实例的操作直接缓存在View中,不会再重复执行

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     @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;
    }
    //ViewHolder 内部类
    class ViewHolder {

    ImageView fruitImage;

    TextView fruitName;

    }

    ListView中的点击事件

  • 比较简单,再MainActivty的onCreate方法中添加

  • 代码如下

    1
    2
    3
    4
    5
    6
    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();
  • setOnItemClickListener为ListView注册了一个监听器,点击发生时回掉 onItemClick()方法,该方法通过position参数判断具体子项。该处是执行Toast


  • 资料来源如下
  • 第一行代码(第二版)

常用控件

TextView

  • TextView 显示文本信息
  • ```
    <TextView
    android:id="@+id/text_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:textSize="24sp"
    android:textColor="#00ff00"
    android:text="This is TextView" />
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    * 效果如下
    ![ScreenShot_20161209145323.png](http://i.loli.net/2019/03/26/5c9a25ffa6833.png)

    #### 常用属性详解
    * android:id 指定当前控件唯一标识符
    * android:layout_width android:layout_height
    指定控件的宽度和高度,取值有
    * match_parent 匹配父布局
    * wrap_content自适应内容
    * fill_parent与match_parent 相同

    * android:gravity 文字对齐方式,
    可取值 top bottom left right center 可以使用 | 同时使用多个属性

    * android:text 文本内容

    * android:textSize android:textColor 文本的大小/颜色

    ### Button
    * [Button](http://developer.android.com/reference/android/widget/Button.html) 按钮
    * 代码
    ```xml
    <Button
    android:id="@+id/button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Button"
    android:textAllCaps="false"/>
  • 效果如下
    2.png

常用属性

  • android:textAllCaps 系统自动转换文本为大写 true/false

按钮响应(3种)

  • 匿名类注册监听器

    • 代码如下
      1
      2
      3
      4
      5
      6
      7
      8
      //新建Button对象 //强制类型转换
      Button button = (Button)findViewById(R.id.button);
      button.setOnClickListener(new View.OnClickListener(){
      @Override
      public void onClick(View v){
      //添加逻辑
      }
      });
  • 接口方式实现

    • 代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
         //继承接口
      public class MainActivity extends AppCompatActivity implements View.OnClickListener{

      @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
      //根据button id处理不同逻辑
      public void onClick(View view){
      switch (view.getId()){
      case R.id.button:
      //处理逻辑
      break;
      default:
      break;
      }
      }
      }
  • android:onClick 匹配
    在xml中指定android:onClick 指定的方法名称匹配,签名必须完全相同
    方法要求:

    • 是公共方法 public
    • 具有空返回值 void
    • 以 View 作为唯一参数(这将是之前点击的 View)
    • 代码如下
      1
      android:onClick="Button_onClick"
      1
      2
      3
      public void Button_onClick(View view){
      //处理逻辑
      }

      EditText

  • EditText

  • 代码如下

    1
    2
    3
    4
    5
    6
    <EditText
    android:id="@+id/edit_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="输入文字"
    android:maxLines="2"/>
  • 效果
    ScreenShot_20161210102606.png

    常用属性

  • android:hint 指定一些提示性文字,再用户未输入时提示。

  • android:maxLines 输入内容最大占用行数

提取输入文本

  • 通过findViewById找到EditText 实例,在处理逻辑中调用EditText.getText方法得到输入内容,再由toString转换为字符串。
  • 代码如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //新建EditTExt对象
    private EditText editText;

    //绑定EditText实例
    editText = (EditText)findViewById(R.id.edit_text);

    //提取文本
    String inputText = editText.getText().toString();
    //Toast显示
    Toast.makeText(MainActivity.this,inputText,Toast.LENGTH_SHORT).show();

ImageView

  • 将图片放入drawable-xhdpi文件夹
  • ImageView
  • 代码
    1
    2
    3
    4
    5
    <ImageView
    android:id="@+id/image_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/img_1"/>
  • 效果如下
    ScreenShot_20161211142408.png

常用属性

  • android:src 引用资源位置

更改ImageView图片

  • 调用 imageView.setImageResource()方法
  • 代码如下
    1
    2
    3
    4
    5
    private ImageView imageView;

    imageView = (ImageView)findViewById(R.id.image_view);

    imageView.setImageResource(R.drawable.img_2);

ProgressBar

  • ProgressBar
  • 代码如下
    1
    2
    3
    4
    5
    6
    <ProgressBar
    android:id="@+id/progress_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="?android:attr/progressBarStyleHorizontal"
    android:max="100"/>
  • ScreenShot_20161211143148.png

常用属性

  • android:visibility 是否可见 取值 visible invisible gone
  • style=”?android:attr/progressBarStyleHorizontal”
    android:max=”100”
    设定显示方式为横向进度条,进度条最大值100

进度条有关设置

  • 显示/隐藏进度条
    progressBar.setVisibility()方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private ProgressBar progressBar;

    progressBar = (ProgressBar)findViewById(R.id.progress_bar);

    //设置ProgressBar
    if(progressBar.getVisibility() == View.GONE){
    progressBar.setVisibility(View.VISIBLE);
    } else {
    progressBar.setVisibility(View.GONE);
    }
  • 设置进度条进度
    1
    2
    3
    int progress = progressBar.getProgress();
    progress +=10;
    progressBar.setProgress(progress);

AlertDialog

  • AlertDialog
  • 代码如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
    dialog.setTitle("This is Dialog");
    dialog.setMessage("something important");
    dialog.setCancelable(false);
    //setPositiveButton设定OK点击事件
    dialog.setPositiveButton("OK",new DialogInterface.OnClickListener(){
    @Override
    public void onClick(DialogInterface dialog,int which){
    }
    });
    //setNegativeButton设定Canncel点击事件
    dialog.setNegativeButton("Canncel",new DialogInterface.OnClickListener(){
    @Override
    public void onClick(DialogInterface dialog,int which){
    }
    });
    //显示 AlertDialog
    dialog.show();
  • ScreenShot_20161211195720.png

ProgressDialog

  • ProgressDialog与AlertDialog类似,但是会额外显示一个进度条

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //新建ProgressDialog对象
    ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
    //设置标题
    progressDialog.setTitle("This is ProgressDialog");
    //设置内容
    progressDialog.setMessage("Loading...");
    //是否可以返回键取消
    progressDialog.setCancelable(false);
    //显示
    progressDialog.show();
  • 效果如图
    ScreenShot_20161211200629.png

  • PS:progressDialog.setCancelable();属性设置为false时表示ProgressDialog无法通过Back键取消。只能通过progressDialog.dismiss()方法取消

四种基本布局

LinearLayout 线性布局

  • 线性方向上依次排列
  • 基础效果如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ljy.com.uilayouttest.MainActivity">
    <Button
    android:id="@+id/button_1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button_1"/>

    <Button
    android:id="@+id/button_2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button_2"/>

    <Button
    android:id="@+id/button_3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button_3"/>

    </LinearLayout>

    基础属性

  • android:orientation LinearLayout 的排列方向,取值有两种 horizontal横向和vertical竖向,不指定 android:orientation时,对排港博客认 horizontal
  • android:layout_gravity _指定控件在布局中的对齐方式。该属性与 LinearLayout 的排列方向有很大关系。
  • android:layout_weight _允许使用比例方式指定控件大小,计算控件大小时,系统非将所有控件的android:layout_weight _值相加,当作基底,计算指定的大小比例
    • EditText和Button
      常见用法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
          <EditText
      android:id="@+id/button_1"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:hint="Button_1"/>

      <Button
      android:id="@+id/button_2"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:text="Button_2"/>
      ScreenShot_20161213100420.png
      指定android:layout_width _为0dp,android:layout_weight=”1”均为1 _平分大小
    • 另一种用法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
          <EditText
      android:id="@+id/button_1"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:hint="Button_1"/>

      <Button
      android:id="@+id/button_2"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Button_2"/>
      将Button的android:layout_width设定为wrap_content,EditText 的android:layout_weight=”1”,EditText会占满整个屏幕剩余部分,在适配屏幕时较常用_

RelativeLayout相对布局

  • 属性较多,以代码形式说明

    相对父布局位置

  • 代码如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ljy.com.uilayouttest.MainActivity">

    <Button
    android:id="@+id/button_1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:text="Button 1"/>
    <Button
    android:id="@+id/button_2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:text="Button 2"/>
    <Button
    android:id="@+id/button_3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="Button 3"/>
    <Button
    android:id="@+id/button_4"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:text="Button 4"/>
    <Button
    android:id="@+id/button_5"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:text="Button 5"/>

    </RelativeLayout>
  • ScreenShot_20161213102007.png
  • android:layout_alignParentLeft=”true”
    android:layout_alignParentTop=”true”
    android:layout_centerInParent=”true”
    android:layout_alignParentBottom=”true”
    android:layout_alignParentRight=”true”
    _简而言之这些属性指定了控件相对父布局的位置

相对控件位置

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ljy.com.uilayouttest.MainActivity">

    <Button
    android:id="@+id/button_3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="Button 3"/>
    <Button
    android:id="@+id/button_1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@id/button_3"
    android:layout_toLeftOf="@id/button_3"
    android:text="Button 1"/>
    <Button
    android:id="@+id/button_2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@id/button_3"
    android:layout_toRightOf="@id/button_3"
    android:text="Button 2"/>

    <Button
    android:id="@+id/button_4"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/button_3"
    android:layout_toLeftOf="@id/button_3"
    android:text="Button 4"/>
    <Button
    android:id="@+id/button_5"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/button_3"
    android:layout_toRightOf="@id/button_3"
    android:text="Button 5"/>

    </RelativeLayout>
  • ScreenShot_20161213103016.png

  • 说明

    • android:layout_above 可以指定一个控件位于指定控件的上方,需要指定ID引用
    • android:layout_below 指定一个控件位于指定控件的下方,id引用
    • android:layout_toLeftOf 指定一个控件位于指定控件的左侧,id引用
    • android:layout_toRightOf 指定一个控件位于指定控件的右侧,id引用。
  • NOTE:当控件去引用另一个控件的ID时,引用控件一定要在前本例中是id/button_3在最前面_

FrameLayout帧布局

  • 所有控件对排港博客认左上角

  • 代码如下
    两个控件重合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
    android:id="@+id/text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView"/>
    <ImageView
    android:id="@+id/image_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@mipmap/ic_launcher"/>
    </FrameLayout>

    ScreenShot_20161213113149.png

  • android:layout_gravity _同样可以应用于FrameLayout中,指定对齐方式

PercentFrameLayout百分比布局

  • PercentFrameLayout并非内置于系统SDK中,使用前要在build.gradle中添加百分比布局的依赖。
  • 修改app/build.gradle文件,在dependencies闭包中增加依赖。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.1'
    //下面一行为增加内容,要与上面的版本号保持一致
    compile 'com.android.support:percent:25.0.1'
    testCompile 'junit:junit:4.12'
    }
    修改完成后,as会开始同步,同步完成即可。
  • PercentFrameLayout继承了FrameLayout的特性,所有控件对排港博客认左上角,需要通过android:layout_gravity _来调整位置

  • 源码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    # PercentFrameLayout 并非系统内置SDK,需要声明完整包路径
    <android.support.percent.PercentFrameLayout
    # 随后定义app的命名空间
    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">

    <Button
    android:id="@+id/button1"
    android:text="Button1"
    android:layout_gravity="left|top"
    app:layout_widthPercent="50%"
    app:layout_heightPercent="50%"/>
    <Button
    android:id="@+id/button2"
    android:text="Button2"
    android:layout_gravity="right|top"
    app:layout_widthPercent="50%"
    app:layout_heightPercent="50%"/>
    <Button
    android:id="@+id/button3"
    android:text="Button3"
    android:layout_gravity="left|bottom"
    app:layout_widthPercent="50%"
    app:layout_heightPercent="50%"/>
    <Button
    android:id="@+id/button4"
    android:text="Button4"
    android:layout_gravity="right|bottom"
    app:layout_widthPercent="50%"
    app:layout_heightPercent="50%"/>

    </android.support.percent.PercentFrameLayout>
  • 效果如图
    ScreenShot_20161213145134.png

  • 与之类似的还有 PercentRelativeLayout,用法不加累赘

自定义控件

引入布局

  • 新建title.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <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 _中使用title.xml
    <include layout="@layout/title"/>

  • 隐藏系统自带标题栏,在mainActivity的onCreat中添加如下代码。

    1
    2
    3
      ActionBar actionBar = getSupportActionBar();
    if (actionBar != null)
    actionBar.hide();
  • 效果如下
    ScreenShot_20161213182554.png

    自定义控件

    • 创建TitleLayout继承自LinearLayout ,代码如下
      1
      2
      3
      4
      5
      6
      7
      8
      9
      public class TitleLayout extends LinearLayout {
      //构造函数
      public TitleLayout(Context context, AttributeSet attributeSet){
      super(context,attributeSet);
      //调用LayoutInflater.from方法构建LayoutInflater对象,
      //再调用inflate加载布局文件
      LayoutInflater.from(context).inflate(R.layout.title,this);
      }
      }
  • 再activity_main.xml _中 添加自定义控件,
    添加自定义控件时要指明控件的完整类名

    1
    2
    3
    <ljy.com.uicustomviews.TitleLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
  • 效果与引入布局文件相同

注册按钮点击事件

  • 在TitleLayout的构造函数添加按钮注册点击事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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通知
    Toast.makeText(getContext(),"clik",Toast.LENGTH_SHORT).show();
    }
    });

倒腾了俩小时,以为梯子坏了,结果。。。GitHub-Pages更新了!!!


原因

  • GitHub-Pages更新, 过滤掉了 source/vendors 目录的访问。
  • issues入口

解决1更新主题

  • 作者更新了到最新的master分支,解决了这个问题,
    So:
    1
    git pull http://github.com/iissnan/hexo-theme-next themes/next

解决2更新本地设置(Next最新版)

  • 将 source/vendors 目录修改成 source/lib (或者其他的名称,只是 lib 我测试了可以使用);同时,修改下主题配置文件_config.yml, 将 _internal: vendors 改成你所修改的名字,例如 _internal: lib。

解决3更新本地设置(Next5.0及以下版本)

  • 将 source/vendors 目录修改成 source/lib (或者其他的名称,只是 lib 我测试了可以使用);同时,修改下主题配置文件_config.yml, *vendors: vendors *改成你所修改的名字,例如 _internal: lib。

Andorid笔记系列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
更新
2016.07.20 第一行代码Activity创建部分完成
2016.07.21 完成添加了button和menu部分,对应P39页
2016.07.22 完成了P55页之前的activity部分。对于activity(二)
2016.08.10 基本搞定,UI除ListView以外的部分。4种布局比较熟悉,不再折腾。
2016.08.11 开始ListView部分
2016.08.14 停更半月
2016.09.02 恢复更新
2016.09.03 Broadcast部分开坑。
2016.09.07 ListView部分除BaseAdapter外完成。BaseAdapter内容,什么时候理解深一点再补上。
2016.09.13 完成了Broadcast对应部分
2016.10.03 重新按照google的文档查漏补缺.
2016.10.03 更名Android笔记,重新开始更新。
2016.12.05 第二行代码开始!
2016.12.07 重新整理Android系列笔记,从0整合
2016.12.08 Activity部分整合完成
2016.12.13 UI部分基础完成,LIstView部分完成
2016.12.16 RecycleView部分,基础完成,有待进一步填坑
2016.12.22 Fragment完成,有一个占坑
2016.12.23 Broadcast完成

累死人的单片机课程设计终于结束了,忙了两天多。就是个数码管+ADC。看看自己两年多前写的程序。。这感觉。用了一天多重构。。。。之前写程序的那个乱啊。。。重新整理下,自己的库函数。记录于此。


1602库函数

1602.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*模 块 名:                LCD1602      */

/*创 建 人:zyy 日期:2014-11-04 */

/*修 改 者:js 日期:2016-07-08 */

/*版 本2.1 */

/*使用方法:首先调用LCD初始化函数void LcdInit();
输入一个double类型数据调用void LcdDisplay(double temp);
显示在第二行;如需修改第一行数据可在LcdDisplay函数中修改 */
/* BUG : 第二行double数据显示最后一位后,会跟随一位乱码 */


#include <stc12c5a60s2.h>
#include <intrins.h>
#include <stdio.h>
#include <math.h>
#define uint unsigned int
#define uchar unsigned char

sbit lcdrs = P1^7;
sbit lcden = P2^4;
sbit rw = P1^6;

#define lcd_data_port P0




void sdelay(uint s); //通用延迟函数

void write_com(uchar com); //并口写数据
void write_data(uchar date); //并口写数据

void LcdInit(); //1602初始化

void LcdDisplay(double temp,uint t); //显示函数

1602.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//1602.c
include <1602.h>

/*模 块 名: LCD1602 */

/*创 建 人:zyy 日期:2014-11-04 */

/*修 改 者:js 日期:2016-07-08 */

/*版 本2.1 */



/**********************************************/
void sdelay(uint s) //通用延迟函数
{
uint x,y;
for(x=s;x>0;x--)
for(y=250;y>0;y--);
}
/**********************************************/
void write_com(uchar com) //并口写数据
{
lcdrs=0;
lcden=0;
lcd_data_port=com;
sdelay(1);
lcden=1;
sdelay(1);
lcden=0;
}
void write_data(uchar date) //并口写数据
{
lcdrs=1;
lcden=0;
lcd_data_port=date;
sdelay(1);
lcden=1;
sdelay(1);
lcden=0;
}


/*********************************************************************
*
* 函 数 名: LcdInit
* 功能描述: LCD初始化
* 函数说明: 初始化数据:0x38 0x0c 0x06 0x01
* 调用函数: sdelay(),write_com(), write_data()
* 输 入: 无
* 返 回: 无
* 设 计 者:zyy 日期:2014-12-23
* 版 本: 1.0
***********************************************************************/
void LcdInit() //1602初始化
{
lcden=0;
rw=0;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
}

/*********************************************************************
*
* 函 数 名: LcdDisplay
* 功能描述: LCD显示
* 函数说明: double类型数据转化为字符串,在lcd第二行显示。
* 调用函数: sprintf(),write_com(),sdelay()
* 全局变量: 无
* 输 入: 一个double类型变量值
* 返 回: 无
* 设 计 者:zyy 日期:2014-12-23
* 修 改 者:js 日期:2016-07-08
* 版 本: 2.0
***********************************************************************/


void LcdDisplay(double temp,uint t) //显示函数
{
static uchar table0[]={"pm "}; //1602对排港博客认第一行
static uchar table1[]={"00.0 "};

uchar num=0;

sprintf(table1,"%f",(double)temp);
sprintf(table1+8,"%2d",(uint)t);


write_com(0x80);
for(num=0;num<15;num++) //第一行刷新
{
write_data(table0[num]);
sdelay(5);
}


write_com(0x80+0x40);
for(num=0;num<16;num++) //第二行显示
{write_data(table1[num]); //0.0195
sdelay(5);
}
}

数码管

  • 对应电路图如下
    QQ.jpg

    shu.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    /*模 块 名:           数码管           */

    /*创 建 人:zy 日期:2014-07-01 */

    /*版 本1.2 */

    /*使用方法:调用 Shu_Display(double js)函数,输入一个double类型数据,
    固定显示格式为小数点前4位及小数点后两位。 */

    /*BUG:显示小数点后第二位时,有时会与输入值小1.原因疑似与fmod()函数有关 */

    #include <stc12c5a60s2.h>
    #include <intrins.h>
    #include <stdio.h>
    #include <math.h>
    #define uint unsigned int
    #define uchar unsigned char


    //端口定义
    #define io_dm P0 //定义LED显示的段码数据脚
    sbit io_shu = P2^3; //数码管开关低电平有效
    sbit io_A = P2^0; //3-8译码器输入
    sbit io_B = P2^1;
    sbit io_C = P2^2;

    sbit io_DP = P0^7; //dp点定义


    extern uchar du_num[15];


    void delay_1ms(uchar x); //1ms延迟函数

    void wei(uchar i); //位选输出函数

    void Shu_Display(double js);//数码管显示函数

    shu.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    #include <shu.h>

    /*模 块 名: 数码管 */

    /*创 建 人:zy 日期:2014-07-01 */

    /*版 本1.2 */

    //显示数字
    uchar du_num[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
    // 0 1 2 3 4 5 6 7 8 9 a b c d e f



    void delay_1ms(uchar x)//1ms延迟函数
    {
    uchar j;
    while(x--){
    for(j=0;j<125;j++)
    {;}
    }
    }

    void wei(uchar i) //位选信号输入
    { //i=i+1;
    switch (i)
    { case 0:{io_A=0;io_B=0;io_C=0;} break;
    case 1:{io_A=1;io_B=0;io_C=0;} break;
    case 2:{io_A=0;io_B=2;io_C=0;} break;
    case 3:{io_A=1;io_B=1;io_C=0;} break;
    case 4:{io_A=0;io_B=0;io_C=1;} break;
    case 5:{io_A=1;io_B=0;io_C=1;} break;
    case 6:{io_A=0;io_B=1;io_C=1;} break;

    default: ; break;
    }

    }


    /*********************************************************************
    *
    * 函 数 名: Shu_Display()
    * 功能描述: 将数字显示到数码管上,显示格式4+2
    * 函数说明: 段选位选,动态刷新数码管
    * 调用函数: delay_1ms fmod()
    * 全局变量: 无
    * 输 入: 1个double类型数据
    * 返 回: 无
    * 设 计 者:zy 日期:2014-07-01
    * 修 改 者:zy 日期:2016-07-08
    * 版 本: 1.2
    ***********************************************************************/
    void Shu_Display(double js)
    {uchar i; //循环变量
    static uchar suff[6]; //数据处理暂存数组
    static uint cuff[4]; //提取各位有关
    static double n=1,y; //与小数截取有关
    static uint count; //暂存

    y=fmod(js,n); //分离js小数部分 存在y中

    count=js; //强制类型转换,取js整数部分

    suff[0] = count/1000; //取千位
    cuff[0] = count%1000;

    suff[1] = cuff[0]/100; //取百位
    cuff[1] = cuff[0]%100;

    suff[2] = cuff[1]/10; //取十位
    suff[3] = cuff[1]%10; //取个位

    count = y*1000; //小数部分。扩大1000倍提取。

    cuff[2] = count%1000;

    suff[4] = cuff[2]/100; //小数点后一位
    cuff[3] = cuff[2]%100;

    suff[5] = cuff[3]/10; //小数点后2位

    io_shu=0; //打开3-8译码器

    for(i=0;i<6;i++)
    {
    wei(i); //位选
    io_dm=du_num[suff[i]];//段选
    if(i==3) io_DP=1;
    delay_1ms(1); //延迟
    }

    }

  • 总算是博客搭建完了,依附在github上,自己的域名什么的先不买了。
  • 评论系统、RSS、留言、统计、访问量 基本有个独立博客的样子了。之前一直以为独立博客要花很长时间才可以完成,其实也就那些东西,真正厉害的是造轮子的人。
  • 马上大四了,反而找到了自己的兴趣,不知道是高兴还是悲伤,做了三年的单片机最后发现最喜欢的是软件,造化弄人吧。 开始更新自己的经历和心情,基本不用担心审查和备案,反正服务器又不在国内。只能说走一步再走一步了用好剩下的时间。前路和期许很难,但是就这么熬着,总有意外的一天。

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment