生存是唯一的长路


资料来源如下

  • 第一行代码(第二版)
  • Android官方文档之App Components(Fragments)

编程环境

  • Android Studio 2.2.3

导语

  • Fragments的艺术之旅

Fragments简介

  • Fragment—片段 是Android 在 Android 3.0(API 11 级)中引入了,主要是为了给大屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。
  • 可以将多个片段组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个片段。可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,可以在 Activity 运行时添加或删除片段(类似于不同 Activity 中重复使用的“子 Activity”)
  • Google官方文档(中文)

创建Fragments

  • fragment创建过程与Activity类似。

  • 创建Fragment,需要继承一个Fragment类,并实现Fragment的生命周期回调方法,如onCreate(), onStart(), onPause(), onStop()等

  • 一般来说,在Fragment中应至少重写以下这些生命周期方法
    必须重写的时onCreateView()方法.

    • onCreate():创建Fragment实例时,系统回调的方法。在该方法中,对一些必要的组件进行初始化
    • onCreateView():Fragment上绘制UI时,回掉该方法。返回一个View对象,表示Fragment的根视图;若Fragment不需要绑定示图,可以返回null
    • onPause():当用户离开Fragment时回调。在该方法中,对Fragment的数据信息做持久化的保存工作

创建一个Fragment类

  • 新建first_fragme 继承自Fragment

  • Android Studio中 对应Fragment 包有两个,选择support-v4 _(这个版本可以再Android版本中保持Fragment特性一致)

  • Fragment并非一定要绑定一个布局文件,下面会提到。

    1
    2
    3
    4
    5
    6
    7
    public class FirstFragment extends Fragment {
    @Override
    public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState){
    View view = inflater.inflate(R.layout.fragment_firest,container,false);
    return view;
    }
    }
  • 与此对应的fragment_firest布局文件 _
    LinearLayout加一个Button,背景颜色设置为了靛蓝色。

  • onCreateView()方法

    • ViewGroup来自宿主Activity容器布局,Fragment的布局将其作为根视图插入至该视图中。
    • Bundle用于回传之前占据该位置的Fragment实例所保存的Bundle信息,当该Fragment的新实例处于resume状态时,该参数被回传
  • inflate() 方法

    • 参数1(int):需要绑定的Layout的资源ID;

    • 参数2(ViewGroup):绑定的Layout布局的父视图;

    • 参数3(boolean):是否需要将参数1的Layout资源依附于,参数2的ViewGroup上,false,表示不依附。(系统已经默认将Layout插入至ViewGroup中,若为true,将添加一层冗余的视图

在XML将fragment添加到activity

  • 在activity_main.xml中添加如下代码。跟添加一个layout没有太大区别。

    1
    2
    3
    4
    5
    6
    <fragment
    android:id="@+id/left_fragment"
    android:name="ljy.com.fragmenttest.fragment_firest"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"/>
  • 至此一个fragment添加完毕。为了对比,再新建一个second_fragment,背景颜色不同,两者同时添加进activity_main中。

  • Screenshot_1476109113.png

  • 一般来说必须为fragment设定唯一的身份标识,以便当宿主Activity为restart状态时可以恢复fragment

    • android:id属性为fragment指定唯一ID
    • android:tag属性为fragment指定唯一字符串标识
    • 未指定,则该fragment的标识为其父容器控件的ID

动态添加碎片


  • 处理片段时,请谨记:Activity 布局必须包含一个可以插入片段的容器 View

  • 添加或移除片段必须使用 FragmentManager 创建 FragmentTransaction, FragmentTransaction将提供添加、移除、替换片段以及执行其他片段事务所需的 API。Activity 内调用 getSupportFragmentManager() 以获取 FragmentManager

    1
    2
    rightfragment right = new rightfragment();
    FragmentManager fragmentManager = getSupportFragmentManager();
  • 调用 beginTransaction() 创建一个 FragmentTransaction事务,并调用 add() 添加一个片段,做好更改准备时,调用 commit()

    1
    2
    3
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout,right);
    transaction.commit();
  • 示例:

    • 首先改造一下activity_main.xml_
      FrameLayout包含一个left_fragment

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
        <FrameLayout
      android:id="@+id/right_layout"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1">

      <fragment
      android:id="@+id/left_fragment"
      android:name="com.example.fragment_text.leftfragment"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      />
      </FrameLayout>
    • 在MainActivity的onCreate方法中添加fragment

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      //新建准备替换的Fragment实例
      rightfragment right = new rightfragment();

      //新建FragmentManager
      FragmentManager fragmentManager = getSupportFragmentManager();
      // 调用beginTransaction() 创建FragmentTransaction
      FragmentTransaction transaction = fragmentManager.beginTransaction();

      //FragmentTransaction内处理添加/替换等。
      transaction.replace(R.id.right_layout,right);

      //最后执行commit()方法
      transaction.commit();

管理Fragments/执行Fragment事务

  • Activity中管理Fragment,使用FragmentManager(

    1
    FragmentManager fragmentManager = getFragmentManager();

    可以实现的操作

    • findFragmentById()方法获取由Activity管辖的绑定了UI的Fragment实例
    • popBackStack()方法将Fragment从后退栈中弹出
    • addOnBackStackChangedListener()方法注册监听器,用于监听后退栈的变化
  • Fragment可实现动态添加、删除、替换 等 操作,每一组向Activity提交的变化称为事务,使用FragmentTransaction这操作事务,调用beginTransaction()开启一个事务

    1
    2
    FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    可实现对Feagment的操作

    • add() //添加
    • remove() //移除
    • replace() //替换
  • 事务要在Activity中生效,调用commit()方法提交

  • commit()方法之前,调用addToBackStack()方法,可以将该事物添加到由Activity管辖的Fragment返回栈中。点击Back键即撤销该事物提交的更改


  • 使用FragmentTransaction操作事务时注意

    • commit()必须在最后调用
    • 一个布局容器中添加多个Fragment,加入的顺序决定了这些Fragment绑定的UI视图在View树中的层级顺序
  • commit()方法提交后,并不会立即执行事务,UI更新只能在主线程中进行,主线程空闲时,才会执行事务操作
    Android也提供了 在UI线程中调用executePendingTransactions()方法,使commit()方法调用后立即执行提交的事务(一般用不到)


与 Activity 通信

  • Fragment 中获取 Activity 实例
    getActivity()方法可以获取到 Activity 实例
    1
    MainActivity activity = (MainActivity) getActivity();
    调用Activity中的试图也很简单,使用findViewById()即可,getActivity()方法返回的既是一个Context对象
    1
    View listView = getActivity().findViewById(R.id.list);

  • 在Fragment中使用Context对象,getActivity()方法,只能是在fragment已经依附于Activity后才能调用。当fragment未依附于某个Activity、或fragment已经处于其生命周期的末尾而不再依附于某个Activity时,调用getActivity()方法会直接返回null

  • Activity 中获取 Fragment 实例()
    FragmentManger提供了findFragmentById()方法

    1
    RightFragment fragment = (RightFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

    如果使用的使support-V4包,则 getFragmentManager()改为getSupportFragmentManager(),代码如下:

    1
    RightFragment fragment = (RightFragment)getSupportFragmentManager().findFragmentById(R.id.example_fragment);
  • Fragment与Frangment之间也可以通过宿主Activity,获取到另一个Fragment实例,调用另一个Fragment中的方法

为Activity创建事件回调

  • 占坑,

Frangment的生命周期

  • 与Activity极为相似,且 Frangment的生命周期与宿主Activity有很大关联

  • 有3种状态

    • Resumed继续:宿主Activity处于running,Fragment处于可见状态
    • Paused暂停:另一个Activity处于前台并获得了焦点,该Fragment的宿主Activity并未被全部遮挡
    • Stopped停止:Fragment不可见或Fragment已被Activity移除,宿主Activity被回收时,Fragment也将被回收
  • fragment_lifecycle.png

宿主Activity的影响

  • activity_fragment_lifecycle.png

重要回调方法

  • onAttach():fragment关联Activity时回调
  • onCreateView():fragment绑定UI视图(加载布局)时回调
  • onActivityCreated():宿主Activity创建完毕 (宿主Activity的onCreate()方法返回) 后调用
  • onDestroyView():与fragment绑定的UI视图被移除时回调
  • onDetach():fragment不再依附于Activity时回调

  • Activity 与Fragment在生命周期之间的最显著差异在于它们在其各自返回栈中的存储方式。
    默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈(以便用户通过返回按钮回退到 Activity),
    Frament仅当您在移除片段的事务执行期间通过调用 addToBackStack() 请求保存实例时,系统才会将Fragment放入由宿主 Activity 管理的返回栈。

  • 一旦Activity处于resume状态时,可以自由地添加或移除fragment,也就是说,只有当Activity的状态为resume时,fragment才能够自由地控制自己的生命周期


资料来源如下

  • 第一行代码(第二版)
  • RecyclerView使用详解—六和敬
  • RecyclerView使用介绍— Jin Yudong
  • Android RecyclerView 使用完全解析 体验艺术般的控件—鸿洋_
  • 创建列表与卡片—Android Developer

编程环境

  • Android Studio 2.2.3

导语

  • RecyclerView内容较ListView更多,初期只能更新一些基础内容,高级的用法随时更新,长期跟进

简介

  • RecyclerView是用于取代ListView的组件,第一次出现是在2014年google I/O大会,内置在是Android L及以上版本的SDK中。
  • 对比与ListView,RecyclerView弥补了ListView中的效率问题,同时支持更多的显示效果,代码逻辑更为清晰

基本使用步骤


  • RecyclerView定义在support库中,使用RecyclerView之前必须在添加依赖
  • build.gradle中添加
    compile 'com.android.support:recyclerview-v7:25.1.0'

  • RecyclerView项目结构如下:
    RecyclerView.png

  • 要使用RecyclerView,需要指定一个Adapter适配器和一个LayoutManager布局管理器

  • Adapter适配器:作用与ListView中使用的Adapter相同,都是将数据与对应item的界面进行绑定
    所不同的是:RecyclerView中适配器必须继承自RecyclerView.Adapter,且 强制使用了ViewHolder

  • LayoutManager布局管理器:每一个item如何进行排列,何时展示和隐藏。
    重用View时,LayoutManager会向Adapter适配器请求新的数据替换旧的数据,避免了View的冗余和频繁调用findViewById

    LayoutManager的引入 使得各种布局排列编写,变的格外容易,这也是RecyclerView优于ListView的一个地方

  • 目前RecyclerView 内置3种LayoutManager:

    • LinearLayoutManager 横向/竖向布局
    • GridLayoutManager 网格布局
    • StaggeredGridLayoutManager 瀑布流布局
  • MainActivity中 RecyclerView 设置

    1. 创建RecyclerView对象
      RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);

    2. 设置LayoutManager显示规则

    3. 设置适配器
      recyclerview.setAdapter(adapter);

简单实例

由一个简单的实例+详细分析

  • 添加依赖
    打开app/build.gradle文件,在dependencies闭包下添加依赖库
    版本与你工程的com.android.support:appcompat-v7:25.1.0版本对应

    1
    compile 'com.android.support:recyclerview-v7:25.1.0'

    之后AndroidStudio会开始同步

  • 添加RecyclerView到xml文件,基本与ListView一致,不过RecyclerView并非内置在SDK中,这里需要写出完整的包路径

    1
    2
    3
    4
    5
    <android.support.v7.widget.RecyclerView
    android:id="@+id/recycle_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />
  • 要展示的依旧是水果+图片形式,
    图片资源在第一行代码第二版源码 /chapter3/ListViewTest\app\src\main\res\drawable-hdpi下,同时将Fruit类和fruit_item.xml一并复制,这里给出两者源码,不再加分析_

    • Fruit类
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      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;
      }
      }
    • 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>
  • 准备RecyclerView适配器,适配器需要继承自 RecyclerView.Adapter,将泛型指定为 .ViewHolder,(ViewHolder为在适配器的一个内部类),并重写3个方法

    1
    2
    3
    public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType)//创建返回ViewHolder实例
    public void onBindViewHolder(ViewHolder holder,int pisition)//数据与界面绑定
    public int getItemCount() // 返回数据的数量

    新建 FruitAdapter 继承自 RecyclerView.Adapter

    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
      //继承自RecyclerView.Adapter,泛型为  FruitAdapter .ViewHolder
    public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    //私有Fruit列表
    private List<Fruit> mFruitList;

    //新建内部类ViewHolder继承自RecyclerView.ViewHolde有每个Item的的所有界面元素
    static class ViewHolder extends RecyclerView.ViewHolder{
    ImageView fruitImage;
    TextView fruitName;
    //ViewHolder构造函数,传入View,通常为RecyclerView子项的外层布局(本例为fruit_item.xml)
    public ViewHolder(View view){
    super(view);
    //findViewById获取 Image/Name实例
    fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
    fruitName = (TextView)view.findViewById(R.id.fruit_name);
    }
    }

    //FruitAdapter构造函数,将数据源传入全局变量mFruitList
    public FruitAdapter(List<Fruit>fruitList){
    mFruitList = fruitList;
    }

    @Override
    //创建ViewHolder实例
    public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
    //加载布局文件
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
    //调用ViewHolder构造函数
    ViewHolder holder = new ViewHolder(view);
    //返回ViewHolder实例
    return holder;
    }

    @Override
    //对 RecyclerView子项进行赋值,有新的子项进入屏幕显示范围时调用
    public void onBindViewHolder(ViewHolder holder,int pisition){
    Fruit fruit = mFruitList.get(pisition);
    holder.fruitImage.setImageResource(fruit.getImageId());
    holder.fruitName.setText(fruit.getName());
    }

    @Override
    //返回 RecyclerView子项数量
    public int getItemCount(){
    return mFruitList.size();
    }

    }
  • MainActivity中使用RecyclerView

    • 创建RecyclerView对象

    • 设置LayoutManager显示规则(默认竖向滚动)

    • 设置适配器

    代码如下

    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
     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);

    //初始化Fruits数据
    initFruits();
    //获取RecyclerView实例
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);

    //创建LinearLayoutManager,并设置入recyclerView
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);

    //新建适配器,并传入
    FruitAdapter adapter = new FruitAdapter(fruitList);
    recyclerView.setAdapter(adapter);
    }

    //初始化Fruits类
    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);
    }
    }
    }
  • 这样最简单的 RecyclerView 就搭建完了效果如下
    ScreenShot_20161216105246.png

  • 代码比ListView稍多,但是逻辑比较清晰。

横向/瀑布流/网格布局

  • 只需要设置相应LayoutManager即可 /手动滑稽

横向

  • 在 LinearLayoutManager 中修改几行代码即可

  • 修改 LinearLayoutManager 代码,添加一行,搞定!

    1
    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
  • 当然需要简单修改一下item的布局文件,LinearLayout改为垂直排列,宽度固定100dp,图片/文字居中
    效果如下
    ScreenShot_20161216105723.png

瀑布流

  • 这需要 StaggeredGridLayoutManager 布局管理器,可以 竖向/横向 滚动

  • 代码如下

    1
    2
    //瀑布流布局,3行,竖向
    StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
  • 调整布局文件,效果如下
    ScreenShot_20161216215538.png

网格

  • 这需要 GridLayoutManager 布局管理器
  • 代码如下
    1
    2
    //网格布局,两行
    GridLayoutManager layoutManager = new GridLayoutManager(this,3);
  • 调整布局文件后,效果如下
    ScreenShot_20161216220321.png

注册点击事件

  • RecyclerView 中并没有提供注册监听器的方法,需要子项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
    29
    30
    31
    32
    33
    34
    35
      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);
    }
    }

    @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);
    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();
    }
    });
    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();
    }
    });
    return holder;
    }


  • 资料来源如下
  • 第一行代码(第二版)
  • 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](https://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](https://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
    11
    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 https://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      */

/*创 建 人:ikuoy 日期: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 */

/*创 建 人:ikuoy 日期: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()
* 输 入: 无
* 返 回: 无
* 设 计 者:ikuoy 日期: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类型变量值
* 返 回: 无
* 设 计 者:ikuoy 日期: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
/*模 块 名:           数码管           */

/*创 建 人:ikuoy 日期: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类型数据
* 返 回: 无
* 设 计 者:ikuoy 日期:2014-07-01
* 修 改 者:ikuoy 日期: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); //延迟
}

}