Android用户界面框架采用MVC(Model-View-Controller)模型,
处理用户输入的控制器(Controller)
显示图像的视图(View)
保存数据和代码的模型(Model)

Android用户界面框架按照"先进先出"的规则从队列中获取时间
单线程用户界面

界面控件
包括定制控件和系统控件

常见的系统控件

TextView,EditText
Button,ImageButton
Checkbox,RadioButton
Spinner
ListView
TabHost

wrap_content表示宽度只能够包含所显示的字符串
fill_content表示宽度将等于父控件的宽度
findViewById()函数能够通过ID引用界面上的任何控件
@+id/RadioGroup01 @标识后面的字符串是ID资源,加号(+)表示需要建立新资源名称,斜杠(/)后面的字符串是新资源的名称
不是新资源,不需要添加加号,要这样表示:
@android:id/RadioGroup01

TextView textView = (TextView)findViewById(R.id.TextView01);
EditText editText = (EditText)findViewById(R.id.EditText01);
textView.setText("用户名:");
editText.setText("Rajan");

注册点击事件

button.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
              textView.setText("Button按钮");
          }
});

将多个按钮注册同一个点击事件的监听器上

 Button.OnClickListener buttonListener = new Button.OnClickListener(){
        @Override
        public void onClick(View v) {
                switch(v.getId()){
                    case R.id.Button01:
                    textView.setText("Button按钮");
                    return;
                case R.id.ImageButton01:
                        textView.setText("ImageButton按钮");
                        return;
                }    
            }};
            
        button.setOnClickListener(buttonListener);
        imageButton.setOnClickListener(buttonListener);

RadioButton是多选框,如何实现多选选项呢

<RadioGroup android:id="@+id/RadioGroup01" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content">

        <RadioButton android:id="@+id/RadioButton01" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content"
            android:text="RadioButton01" >
        </RadioButton>

        <RadioButton android:id="@+id/RadioButton02" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content"
            android:text="RadioButton02" >
        </RadioButton>

</RadioGroup>

Spinner下拉菜单
让数据和组件绑定用到了适配器

Spinner spinner = (Spinner) findViewById(R.id.Spinner01);   
List<String> list  = new ArrayList<String>();
list .add("Spinner子项1");
list .add("Spinner子项2");
list .add("Spinner子项3");
        
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list );
//simple_spinner_dropdown_item是Android内置的浮动菜单的显示方式
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//adapter.setDropDownViewResource(android.R.layout.simple_spinner_item);
spinner.setAdapter(adapter);

关于组件的设置事件
setText
setAdapter
setOnClickListener
setOnItemClickListener

关于适配器的设置事件
setDropDownViewResource
AdapterView.OnItemClickListener()

ListView 用于垂直显示的列表控件

final TextView textView = (TextView)findViewById(R.id.TextView01);
ListView listView = (ListView)findViewById(R.id.ListView01);
        
List<String> list  = new ArrayList<String>();
    list .add("ListView子项1");
    list .add("ListView子项2");
    list .add("ListView子项3");
        
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list );

listView.setAdapter(adapter);

给ListView子项设置点击事件监听器

        
AdapterView.OnItemClickListener listViewListener = new AdapterView.OnItemClickListener(){
            @Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { 
                String msg = "父View:"+arg0.toString()+"\n"+"子View:"+arg1.toString()+"\n"+"位置:"+String.valueOf(arg2)+",ID:"+String.valueOf(arg3);
                textView.setText(msg);
        }};
            
listView.setOnItemClickListener(listViewListener);
}

TabHost
Tab标签页是界面设计中经常使用的界面控件,实现多个分页之间的切换
Inflate将一个layout.xml布局文件变为一个View对象(ui界面)

方式一:

@SuppressWarnings("deprecation")
public class TabDemoActivity extends TabActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        TabHost tabHost = getTabHost();
        
        LayoutInflater.from(this).inflate(R.layout.tab1, tabHost.getTabContentView(),true);
        LayoutInflater.from(this).inflate(R.layout.tab2, tabHost.getTabContentView(),true);
        LayoutInflater.from(this).inflate(R.layout.tab3, tabHost.getTabContentView(),true);
        
        tabHost.addTab(tabHost.newTabSpec("TAB1").
                setIndicator("线性布局").setContent(R.id.layout01));
        tabHost.addTab(tabHost.newTabSpec("TAB2").
                setIndicator("绝对布局").setContent(R.id.layout02));
        tabHost.addTab(tabHost.newTabSpec("TAB3").
                setIndicator("相对布局").setContent(R.id.layout03));

        
    }
}

方式二:

@SuppressWarnings("deprecation")
public class TabDemo2Activity extends TabActivity  {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        

        TabHost tabHost = getTabHost(); 
        
        tabHost.addTab(tabHost.newTabSpec("TAB1").
                setIndicator("线性布局").setContent(new Intent().setClass(this, Tab1Activity.class)));
        tabHost.addTab(tabHost.newTabSpec("TAB2").
                setIndicator("绝对布局").setContent(new Intent().setClass(this, Tab2Activity.class)));
        tabHost.addTab(tabHost.newTabSpec("TAB3").
                setIndicator("相对布局").setContent(new Intent().setClass(this, Tab3Activity.class)));


    }
}

同时在xml的设置也要

<TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
<FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:padding="5dp" />

在AnroidManifest.xml文件中也要添加

<activity  android:name=".Tab1Activity" />
<activity  android:name=".Tab2Activity" />
<activity  android:name=".Tab3Activity" />

界面布局

  • 线性布局
    <LinearLayout> </LinearLayout>
  • 框架布局
    最简单的界面布局,存放一个元素的空白空间,子元素的位置只能放置在空白空间的左上角
    <FrameLayout> </FrameLayout>
  • 表格布局
    表格布局支持嵌套
    添加一个表格布局,无需修改布局的属性值,Id属性为TableLayout01、Layout_width、Layout_height属性都为wrap_content
    <TableLayout> </TableLayout>

    <TableLayout android:id="@+id/TableLayout01" 
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent" 
      xmlns:android="http://schemas.android.com/apk/res/android">
      <TableRow android:id="@+id/TableRow01" 
          android:layout_width="wrap_content" 
          android:layout_height="wrap_content">
          <TextView android:id="@+id/label" 
              android:layout_height="wrap_content" 
              android:layout_width="160dip"
              android:gravity="right" 
              android:text="用户名:" 
              android:padding="3dip" >
          </TextView>
          <EditText android:id="@+id/entry" 
              android:layout_height="wrap_content" 
              android:layout_width="160dip"
              android:padding="3dip" >
          </EditText>
      </TableRow>
      <TableRow android:id="@+id/TableRow02" 
          android:layout_width="wrap_content" 
          android:layout_height="wrap_content">
          <Button android:id="@+id/ok" 
              android:layout_height="wrap_content" 
              android:padding="3dip" 
              android:text="确认">
          </Button>
          <Button android:id="@+id/Button02" 
              android:layout_width="wrap_content" 
              android:layout_height="wrap_content" 
              android:padding="3dip" 
              android:text="取消">
          </Button>
      </TableRow>
    
    </TableLayout>

    android:padding表示与其他元素的间隔距离

  • 相对布局

layout_below 在···的下方
layout_toLeftOf 在···的左边
layout_alignTop 与···在相同的水平位置

<RelativeLayout android:id="@+id/RelativeLayout01" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView android:id="@+id/label" 
        android:layout_height="wrap_content" 
        android:layout_width="fill_parent" 
        android:text="用户名:">
    </TextView>
    <EditText android:id="@+id/entry" 
        android:layout_height="wrap_content" 
        android:layout_width="fill_parent" 
        android:layout_below="@id/label">
    </EditText>
    <Button android:id="@+id/cancel" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:layout_alignParentRight="true" 
        android:layout_marginLeft="10dip" 
        android:layout_below="@id/entry" 
        android:text="取消" >
    </Button>
        <Button android:id="@+id/ok" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:layout_toLeftOf="@id/cancel" 
        android:layout_alignTop="@id/cancel"
        android:text="确认">
    </Button>
</RelativeLayout>
  • 绝对布局
    通过指定界面元素的坐标位置,来确定用户界面的整体布局
    layout_x
    layout_y

    <AbsoluteLayout android:id="@+id/absolutelayout01"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      xmlns:android="http://schemas.android.com/apk/res/android">
      <TextView android:id="@+id/label" 
          android:layout_x="40dip" 
          android:layout_y="40dip" 
          android:layout_height="wrap_content" 
          android:layout_width="wrap_content"         
          android:text="用户名:">
      </TextView>
      <EditText android:id="@+id/entry" 
          android:layout_x="40dip" 
          android:layout_y="60dip" 
          android:layout_height="wrap_content" 
          android:layout_width="150dip">
      </EditText>
          <Button android:id="@+id/ok" 
          android:layout_width="70dip"
          android:layout_height="wrap_content" 
          android:layout_x="40dip" 
          android:layout_y="120dip" 
          android:text="确认">
      </Button>
      <Button android:id="@+id/cancel" 
          android:layout_width="70dip"
          android:layout_height="wrap_content" 
          android:layout_x="120dip" 
          android:layout_y="120dip"  
          android:text="取消" >
      </Button>
    
    </AbsoluteLayout>
  • 网格布局
    对比css
    android:ems
    使 TextView 正好有这么多 em 宽,最终的宽度就变成了,传入的ems*行高

    <GridLayout
          xmlns:android="http://schemas.android.com/apk/res/android"
    
          android:layout_width="match_parent"
          android:layout_height="match_parent"
    
          android:useDefaultMargins="true"
          android:columnCount="4" >
    
          <TextView  
              android:layout_columnSpan="4"
              android:layout_gravity="center_horizontal"
              android:text="这是关于GroidLayout的示例"
              android:textSize="20dip" />
     
             <TextView
              android:text="用户名:"
              android:layout_gravity="right" />
    
          <EditText
              android:ems="8"
              android:layout_columnSpan="2"/>
    
          <TextView
              android:text="密码:"
              android:layout_column="0"
              android:layout_gravity="right"/>
    
      <EditText
              android:ems="8"
              android:layout_columnSpan="2" />
    
      <Button
              android:text="清空输入"
              android:layout_column="1"
              android:layout_gravity="fill_horizontal"/>
    
      <Button
              android:text="下一步"
              android:layout_column="2"
              android:layout_gravity="fill_horizontal"/>
    
    </GridLayout>

菜单

  • 选项菜单
    在Android4.0以后选项菜单只出现浮动菜单
    Activity初始化先调用onCreateOptionsMenu()函数初始化自身的菜单系统,操纵栏的初始化代码也在其中

    public boolean onCreateOptionsMenu(Menu menu) {
          MenuInflater inflater = getMenuInflater();
          inflater.inflate(R.menu.main_menu, menu);//R.menu.main_menu即main_menu.xml文件
          return true;
      }

onOptionsItemSelected函数在每次用户点击菜单子项时都会被调用

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        TextView label = (TextView)findViewById(R.id.label);
        
        switch (item.getItemId()) {
        case R.id.main_menu_0:
            label.setText("打印,菜单ID:" + item.getItemId());
            return true;
        case R.id.main_menu_1:
            label.setText("新建,菜单ID:" + item.getItemId());
            return true;
        case R.id.main_menu_2:
            label.setText("邮件,菜单ID:" + item.getItemId());
            return true;
        case R.id.main_menu_3:
            label.setText("设置,菜单ID:" + item.getItemId());
            return true;
        case R.id.main_menu_4:
            label.setText("订阅,菜单ID:" + item.getItemId());
            return true;
        default:
            return false;
        }
    }

除了使用XML文件的菜单资源,还可以在代码中动态生成菜单

  1. 在代码中定义菜单ID
  2. 在onCreateOptionsMenu()函数中添加选项菜单,并设置菜单的标题和图标等信息,第一个参数是需要显示的快捷菜单,第二个参数是用户点击的界面元素,第三个参数是所选择界面元素的额外信息
  3. add()函数第一个参数是组id,第二个参数是子项id,每个菜单的唯一标识,第三个参数是菜单子项在选项菜单中的排序顺序,第四个菜单是菜单子项的选项标题

     final static int MENU_00 = Menu.FIRST;
     final static int MENU_01 = Menu.FIRST+1;
     final static int MENU_02 = Menu.FIRST+2;
     final static int MENU_03 = Menu.FIRST+3;
     final static int MENU_04 = Menu.FIRST+4;
    
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         menu.add(0,MENU_00,0,"打印").setIcon(R.drawable.pic0);
         menu.add(0,MENU_01,1,"新建").setIcon(R.drawable.pic1);
         menu.add(0,MENU_02,2,"邮件").setIcon(R.drawable.pic2);
         menu.add(0,MENU_03,3,"设置").setIcon(R.drawable.pic3);
         menu.add(0,MENU_04,4,"订阅").setIcon(R.drawable.pic4);
         return true;
     }
  4. 子菜单
    也就是二级菜单
    用xml文件描述菜单结构

    <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:id="@+id/main_menu_0"
           android:icon="@drawable/pic0"
           android:title="设置" >
          <menu>
             <item android:id="@+id/sub_menu_0_0"
                   android:icon="@drawable/pic4"
                   android:title="打印" />
          </menu>
      </item>        
        <item android:id="@+id/main_menu_1"
           android:icon="@drawable/pic1"
           android:title="新建" >
         <menu>
             <item android:id="@+id/sub_menu_1_0"
                 android:icon="@drawable/pic2"
                   android:title="邮件" />
            
             <item android:id="@+id/sub_menu_1_1"
                   android:icon="@drawable/pic3"
                   android:title="订阅" />
         </menu> 
      </item>     
    </menu>

通过代码实现子菜单
addSubMenu()的四个参数含义

  • 组id
  • 菜单项的id
  • 显示排序
  • 菜单显示标题

    final static int MENU_00 = Menu.FIRST;
      final static int MENU_01 = Menu.FIRST+1;
      final static int SUB_MENU_00_01 = Menu.FIRST+2;
      final static int SUB_MENU_01_00 = Menu.FIRST+3;
      final static int SUB_MENU_01_01 = Menu.FIRST+4;
    
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
             
          SubMenu sub1 = (SubMenu) menu.addSubMenu(0,MENU_00,0,"设置")
                  .setHeaderIcon(R.drawable.pic3);
          sub1.add(0,SUB_MENU_00_01 ,0,"打印").setIcon(R.drawable.pic0);
          
          SubMenu sub2 = (SubMenu) menu.addSubMenu(0,MENU_01,1,"新建")
                  .setHeaderIcon(R.drawable.pic1);
          sub2.add(0,SUB_MENU_01_00 ,0,"邮件").setIcon(R.drawable.pic2);
          sub2.add(0,SUB_MENU_01_01 ,0,"订阅").setIcon(R.drawable.pic4);
    
          return true;
      }
  • 快捷菜单
    onCreateContextMenu() 在快捷菜单中添加菜单子项和子菜单
    onContextItemSelected()相应菜单选择事件
重载onCreateContextMenu()函数初始化菜单项,函数参数解释如下:
  • 需要显示的快捷菜单
  • 用户点击的界面元素
  • 所选择界面元素的额外信息

    /** Called when the activity is first created. */
      final static int CONTEXT_MENU_1 = Menu.FIRST;
      final static int CONTEXT_MENU_2 = Menu.FIRST+1;
      final static int CONTEXT_MENU_3 = Menu.FIRST+2;
      TextView LabelView = null;
      
      @Override
      public void onCreateContextMenu(ContextMenu menu, 
              View v, ContextMenuInfo menuInfo){
          menu.setHeaderTitle("快捷菜单标题");
          menu.add(0, CONTEXT_MENU_1, 0,"菜单子项1");
          menu.add(0, CONTEXT_MENU_2, 1,"菜单子项2");
          menu.add(0, CONTEXT_MENU_3, 2,"菜单子项3");
      }

重载onContextItemSelected()函数响应菜单选择事件

@Override
    public boolean onContextItemSelected(MenuItem item){
        switch(item.getItemId()){
        case CONTEXT_MENU_1:
            LabelView.setText("菜单子项1");
            return true;
        case CONTEXT_MENU_2:
            LabelView.setText("菜单子项2");
            return true;
        case CONTEXT_MENU_3:
            LabelView.setText("菜单子项3");
            return true;
        }
    return false;
    }

使用registerForContextMenu(),将快捷菜单注册到某个控件上去

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        LabelView = (TextView)findViewById(R.id.label);
        registerForContextMenu(LabelView);
    }

操纵栏与Fragment
隐藏Activity的操纵栏

<activity android:theme="@android:style/Theme.Holo.NoActionBar">

或者直接在java代码中添加

ActionBar actionBar=getActionBar();
actionBar.hide();

将选项菜单的菜单项标识为操纵栏中只需在xml文件中添加android:showAsAction="ifRoom|withText"

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/main_menu_0"
          android:icon="@drawable/pic0"
          android:title="打印" 
          android:showAsAction="ifRoom|withText"/>
</menu>

在操纵栏上点击某一图标跳转到相应的布局
例如在操纵栏上添加菜单项,点击这个菜单项,出现文字输入功能
添加代码android:actionLayout="@layout/printview",其中printview是printview.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/main_menu_0"
          android:icon="@drawable/pic0"
          android:title="打印" 
          android:showAsAction="ifRoom|withText"
          android:actionLayout="@layout/printview"/>
</menu>

Fragment
使用Fragment可以将两部分功能合并到同一个Activity中实现
每个Fragment都有自己的布局和生命周期回调函数,同一个Fragment可以放置到多个不同的Activity中
创建Fragment需要继承Fragment的基类,且实现onCreate(),onCreateView(),onPause()生命周期回调函数

如何在一个Activity中同时加载两个Fragment?
首先在main.xml增加两个fragment组件
image.png


 <fragment android:name ="edu.hrbeu.FragmentDemo.AFragment"
            android:id="@+id/fragment_a" 
            android:layout_weight="1"
            android:layout_width="0px" 
            android:layout_height="match_parent" />

<fragment android:name ="edu.hrbeu.FragmentDemo.BFragment"
            android:id="@+id/fragment_b" 
            android:layout_weight="1"
            android:layout_width="0px" 
            android:layout_height="match_parent" />

在主函数中加载这个main.xml

public class FragmentDemoActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);//!!!
    }
}

通过main.xml找到Fragment实现类,调用onCreateView()函数绘制界面元素
LayoutInflater主要是用于加载布局的

public class AFragment extends Fragment{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.frag_a, container, false);
    }

}

Tab导航栏

使用操作栏和Fragment实现Tab导航栏

public class FragmentTabActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        final ActionBar bar = getActionBar();
        bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);//第一个参数表示显示,第二个参数表示不显示

        bar.addTab(bar.newTab()
                .setText("Fragment A")
                .setTabListener(new TabListener<AFragment>(
                        this, "fa",AFragment.class)));
        bar.addTab(bar.newTab()
                .setText("Fragment B")
                .setTabListener(new TabListener<BFragment>(
                        this, "fb", BFragment.class)));

        if (savedInstanceState != null) {
            bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
        }
    }
    
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
    }
    
    public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
        private final Activity mActivity;
        private final String mTag;
        private final Class<T> mClass;
        private final Bundle mArgs;
        private Fragment mFragment;

        public TabListener(Activity activity, String tag, Class<T> clz) {
            this(activity, tag, clz, null);
        }

        public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) {
            mActivity = activity;
            mTag = tag;
            mClass = clz;
            mArgs = args;

            mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
            if (mFragment != null && !mFragment.isDetached()) {
                FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
                ft.detach(mFragment);
                ft.commit();
            }
        }

        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
                ft.add(android.R.id.content, mFragment, mTag);
            } else {
                ft.attach(mFragment);
            }
        }

        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
            if (mFragment != null) {
                ft.detach(mFragment);
            }
        }

        public void onTabReselected(Tab tab, FragmentTransaction ft) {
            Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
        }
    }
}

界面事件
onKey()
onTouch()
ACTION_DOWN、ACTION_UP、ACTION_MOVE 是这样被获取的(event.getAction())

常见问题

  1. 抽象类
    在C++中,含有纯虚拟函数的类称为抽象类,它不能生成对象;
    在java中,含有抽象方法的类称为抽象类,同样不能生成对象。
    定义的接口的方法全是抽象的方法,要显示声明,用关键字abstruct
    一个类只要有抽象的方法,就说明他是抽象类,抽象类可以包括非抽象类的方法
  2. 接口
    在Java中,只有实现了接口的所有方法,才算实现了这个方法

3.b7d1f6a6aea0b24861fcbdbb356cbe5.png

答案: 这三个方法安卓自动调用的,实现了对应的接口,将其实例化(就是让类由抽象变具体的过程),安卓就会根据用户的点击事件调用起对应的方法

4.6d621082a9a1639884ff38d7d993f2a.png

答案:这个this指的是当前TabListener的TabListener函数,对应于super是父类的对应函数,因为这是要实现接口,就要把接口下的所有方法给实现。

需要掌握的知识点
image.png


自由自在像小鸟
4 声望1 粉丝