Android开发 - 扩展用户体验

关键字:ActionBar、Tab键、导航、菜单和操作栏、全屏显示、对话框、Dialog、AlertDialog、DialogFragment、Notification

 

  • 自定义操作栏
  • 使用操作栏作为应用程序的导航
  • 使用Android菜单系统
  • 选择操作栏的操作
  • 创建身临其境的应用程序
  • 创建和显示对话框
  • 显示Toast
  • 使用Notification Manager通知用户应用程序事件
  • 创建连续的和不间断的Notification

 

 

操作栏简介

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
</application>

如果任意的Activity使用了(默认的)Theme.Holo主题,并且它的应用程序的目标(或最小)SDK版本为11或者更高,那么它的操作栏是启用的。

<uses-sdk android:targetSdkVersion="11"/>

 

想要在运行时设置操作栏的可见性,可以使用它的show和hide方法。

ActionBar actionBar = getActionBar();
// 隐藏操作栏
actionBar.hide();
// 显示操作栏
actionBar.show();

不包含操作栏的主题Theme.Holo.NoActionBar

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

 

自定义操作栏

默认情况下,操作栏是通过使用应用程序或者Activity中指定的android:icon属性来显示Drawable的,旁边则是主题背景上相应的android:label属性。
可以使用android:logo属性指定一个可选的图形。和正方形的图标不同,对于徽标图形的宽度是没有限制的,但最好限制它的宽度大概为图标宽度的两倍。
通过在运行时使用setDisplayShowTitleEnable方法来设置标题是否显示。

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(false);
//设置徽标显示
actionBar.setDisplayUseLogoEnabled(displayLogo);
//隐藏图标和徽标
actionBar.setDisplayShowHomeEnable(false);
//设置子标题
actionBar.setSubtitle("Inbox");
//设置主标题
actionBar.setTitle("Label:important");
//设置操作栏背景色
Drawable myDrawable = r.getDrawable(R.drawable.gradient_header);
actionBar.setBackgroundDrawable(myDrawable);
//操作栏会拉伸你提供的图像,因此最好是创建一个可拉伸的Drawable。

 

启用拆分操作栏模式

<activity
    android:label="My Activity"
    android:name=".ActionBarActivity"
    android:logo="@drawable/ic_launcher"
    android:uiOptions="splitActionBarWhenNarrow">
</activity>

【图片】
操作栏的布局是由运行时计算和执行的,并且可能根据宿主设备的方向和在运行时对操作栏的配置而改变。

 

自定义操作栏来控制应用程序的导航行为
大多数情况下,应用程序图标应该充当返回到“主”Activity的快捷方式,通常是回到Activity栈的根部。要想应用程序图标能够被单击,需要调用操作栏的setHomeButtonEnable方法。

actionBar.setHomeButtonEnable(true);

应用程序图标/徽标的单机由系统作为一个特殊的菜单项单击事件而广播。菜单项的选择是由Activity中的onOptionsItemSelected处理程序进行处理的,其中会把传入的菜单项参数的ID设置为android.R.id.home。

public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
      case (android.R.id.home) :
        Intent intent = new Intent(this, ActionBarActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
        return true;
      default: return super.onOptionsItemSelected(item);;
    }
}

 

启用应用程序图标的“向上”导航功能

//在希望启用返回导航功能的时候,最好使用图标而不是徽标
actionBar.setDisplayUseLogoEnable(false);
//启用应用程序图标的“向上”导航功能
actionBar.setDisplayHomeAsUpEnable(true);

导航的处理由你实现。操作栏依然会触发菜单项的onOptionsItemSelected处理程序,并使用android.R.id.home作为标识符。
这种行为也引入了一定的风险,特别是当用户有多种方式访问一个特殊的Activity时。如果有顾虑的话,向上行为就应该和返回按钮的行为一致。在所有的情况下,导航行为应该是可以预见的。

 

使用导航Tab键

除了应用程序图标导航,操作栏还提供了导航Tab键和下拉列表。
【图片】
要想配置操作栏以显示Tab键,需要调用它的setNavigationMode方法,并指定参数为ActionBar.NAVIGATION_MODE_TABS。Tab键应该提供应用程序的上下文信息,因此最好同样禁用标题文本。

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(false);

Tab键导航是通过ActionBar的addTab方法添加到操作栏上的。首先创建一个新的Tab并使用它的setText和setIcon方法来设置要显示的标题和图像。另外,还可以使用setCustomView方法,用自定义的View代替操作栏标准的文本和图像布局。

tabOne.setText("First Tab")
      .setIcon(R.drawable.ic_launcher)
      .setContentDescription("Tab the First")
      .setTabListener(
         new TabListener<MyFragment>
           (this, R.id.fragmentContainer, MyFragment.class));

actionBar.addTab(tabOne);

Tab键的切换是通过TabListener来处理的,它允许创建Fragment事务来响应Tab键的选中、未选中和重新选中操作。

  public static class TabListener<T extends Fragment> 
    implements ActionBar.TabListener {
    
    private MyFragment fragment;
    private Activity activity;
    private Class<T> fragmentClass;
    private int fragmentContainer;

    public TabListener(Activity activity, int fragmentContainer, 
                       Class<T> fragmentClass) {

      this.activity = activity;
      this.fragmentContainer = fragmentContainer;
      this.fragmentClass = fragmentClass;
    }

    // 当一个新的Tab键被选中时调用
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
      if (fragment == null) {
        String fragmentName = fragmentClass.getName();
        fragment =
          (MyFragment)Fragment.instantiate(activity, fragmentName);
        ft.add(fragmentContainer, fragment, null);
        fragment.setFragmentText(tab.getText());
      } else {
        ft.attach(fragment);
      }
    }

    // 当其他的Tab键被选中时,在当前选中的Tab键上调用
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
      if (fragment != null) {
        ft.detach(fragment);
      }
    } 

    // 当选中的Tab键被再次选中时调用
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
      // TODO: 当选中的Tab键被再次选中时响应
    }
  }

注意,不需要执行在每个处理程序中创建的Fragment事物 - 操作栏会在必要的时候创建它。

 

使用下拉列表导航

使用操作栏的下拉列表可以代替导航Tab键。
要想设置操作栏以显示一个下拉列表,调用它的setNavigationMode方法,并指定参数为ActionBar.NAVIGATION_MODE_LIST

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

操作栏的下拉列表实现非常像Spinner - Spinner视图每次只显示一个子选项,比关切允许用户从众多的选项中选择这个子选项。如果Array Adapter或者Simple Cursor Adapter一样,可以通过创建一个新的实现了SpinnerAdapter接口的适配器来填充下拉列表。

要想将这个适配器分配给操作栏并处理选择事件,可以调用操作栏的setListNavigationCallbacks,并传入适配器和一个OnNavigationListener。

// 选择下拉列表的导航模式
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
    
// 创建一个新的SpinnerAdapter,它包含了下拉列表中显示的值。
ArrayAdapter dropDownAdapter = ArrayAdapter.createFromResource(this,
                                      R.array.my_dropdown_values,
                                      android.R.layout.simple_list_item_1);
    
// 分配回调来处理下拉列表的选择
actionBar.setListNavigationCallbacks(dropDownAdapter, new OnNavigationListener() {
    public boolean onNavigationItemSelected(int itemPosition, 
                                            long itemId) {
      // 根据选择的下拉列表项的位置修改UI
      return true;
    }
}); 

当用户从下拉列表中选择一项时,onNavigationItemSelected处理程序就会被触发。根据新选择项使用itemPosition和itemId参数确定该如何使用UI。

 

使用自定义导航View

操作栏还允许使用setCustomView方法添加自己的自定义View(包括布局)

actionBar.setCustomView(R.layout.my_custom_navigation);

 

创建并使用菜单和操作栏操作项

创建菜单

要想给Activity添加一个菜单,需要重写它的onCreateOptionsMenu处理程序。

static final private int MENU_ITEM = Menu.FIRST;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
  super.onCreateOptionsMenu(menu);

  menu.add(0,0,0,"AAA");
  menu.add(0,0,1,"BBB");
  menu.add(1,0,1,"CCC");

  return true;
}

与Menu对象一样,调用add返回的每一个MenuItem在下一次调用onCreateOptionsMenu之前都是有效的。可以通过将某个菜单项的ID传递给菜单的findItem方法来寻找一个特定的菜单项,而不用维护对每一个菜单项的引用。

 

指定操作栏的操作

要想把菜单项指定为操作栏的操作,可以使用它的setShowAsActionFlags方法。

MenuItem menuItem=menu.add(0,0,0,"AAA");
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

 

菜单项选项

复选框

MenuItem menuItemB=menu.add(0,0,1,"BBB");
menuItemB.setCheckable(true);

单选按钮

menu.add(0,4,Menu.NONE,"RadioButton 1");
menu.add(0,5,Menu.NONE,"RadioButton 2");
menu.add(0,6,Menu.NONE,"RadioButton 3").setChecked(true);

menu.setGroupCheckable(0,true,true);

扼要标题

扼要标题可以指定只在图标菜单中或者作为操作栏操作现实的额文本。普通的标题只在溢出菜单或者展开菜单显示的菜单项中显示。

MenuItem menuItem=menu.add(0,0,0,"");
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menuItem.setTitleCondensed("Short Title");

图标

图标只在图标菜单中或者作为一个操作显示,不能出现在展开菜单或者自菜单中。

MenuItem menuItem=menu.add(0,0,0,"");
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menuItem.setIcon(android.R.drawable.btn_star_big_on);

菜单项单击侦听器

当菜单项被选择的时候就会执行这个事件处理程序。但是出于效率原因,并不鼓励这么做;相反,菜单项选择应该被onOptionsItemSelected处理程序处理。

menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem item) {
        //如果经过处理,则返回true,否则返回false
        return false;
    }
});

Intent

当单击一个菜单项之后,如果单击时间既没有被MenuItemClickListener处理程序处理,也没有被Activity的onOptionsItemSelected处理程序处理,就会触发分配给这个菜单项的Intent。当Intent触发时,Android将会执行startActivity,并传入指定的Intent。

menuItem.setIntent(new Intent(this,MainActivity.class));

 

添加操作View和操作提供程序

通过使用菜单项的setActionView方法,可以使用任意的View或布局代替表示操作的图标/文本,此时应该传入一个View示例或者一个资源布局。

MenuItem menuItem=menu.add(0,0,0,"");
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menuItem.setActionView(R.layout.my_action_view);
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Switch
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="New Switch"
        android:id="@+id/switch1" />
</LinearLayout>

 

在Fragment中添加菜单项

想要注册Fragment成为选项菜单的贡献者,可以在其onCreate处理程序中调用setHasOptionsMenu方法。

public class myFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setHasOptionsMenu(true);
    }
}

然后重写onCreateOptionsMenu处理程序,填充菜单和操作栏。在程序运行时,系统会汇总Activity和它的每个组建Fragment所提供的菜单项。

 

使用XML定义菜单层次结构

比起在代码中构造菜单,最好是把菜单层次结构定义为XML资源。与布局和其他资源一样,这使得能够为不同的硬件配置、语言和位置创建不同的菜单。
菜单资源存储为项目的res/menu文件夹中的XML文件。每个菜单层次结构必须创建为一个单独的文件,小写文件名就是其资源标识符。

<?xml version="1.0" encoding="utf-8"?>
<!-- Listing 10-15: Defining a Menu Hierarchy in XML  -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:id="@+id/action_item"
    android:icon="@drawable/action_item_icon"
    android:title="@string/action_item_title"
    android:showAsAction="ifRoom">
  </item>
  <item
    android:id="@+id/action_view_item"
    android:icon="@drawable/action_view_icon"
    android:title="@string/action_view_title"
    android:showAsAction="ifRoom|collapseActionView"
    android:actionLayout="@layout/my_action_view">
  </item>
  <item
    android:id="@+id/action_provider_item"
    android:title="Share"
    android:showAsAction="always"
    android:actionProviderClass="android.widget.ShareActionProvider">
  </item>
  <item
    android:id="@+id/item02"
    android:checkable="true" 
    android:title="@string/menu_item_two">
  </item>
  <item
    android:id="@+id/item03"
    android:numericShortcut="3"
    android:alphabeticShortcut="3"
    android:title="@string/menu_item_three">
  </item>
  <item
    android:id="@+id/item04"
    android:title="@string/submenu_title">
    <menu>
      <item
        android:id="@+id/item05"
        android:title="@string/submenu_item">
      </item>
    </menu>
  </item>
</menu>

要使用菜单资源,可以在onCreateOptionsMenu或者onCreateContextMenu处理程序中使用MenuInflater类。

public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu); 

    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.my_menu, menu);

    return true;
}

 

动态更新菜单项

通过重写Activity的onPrepareOptionsMenu方法,可以在每次显示菜单之前根据应用程序的当前状态对菜单进行修改。这可以动态地启用/禁用每个菜单项,设置每个菜单项的可见性,以及修改文本。
注意:每次菜单按钮被单击、显示溢出菜单、或者创建菜单栏的时候,都会触发onPrepareOptionsMenu方法。

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);

    MenuItem menuItem = menu.findItem(MENU_ITEM);

    // [ ... 修改菜单项 ... ]

    return true;
}

 

处理菜单选择

Android使用单一的事件处理程序来处理菜单栏操作、溢出菜单和Activity菜单选择,该处理程序是onOptionsItemSelected。被选择的菜单项会作为MenuItem参数传递给这个方法。
要多菜单栏选择做出响应,首先要把item.getItemId的值和在填充菜单是使用的菜单项标识符(或者如果实在XML中定义的菜单,就是用资源标识符)进行比较,然后再相应的做出响应。

public boolean onOptionsItemSelected(MenuItem item) {
    super.onOptionsItemSelected(item);

    // 找到选择的菜单项
    switch (item.getItemId()) {
        // 检查每个已知的菜单项
        case (android.R.id.home) :
            Intent intent = new Intent(this, ActionBarActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
            return true;
        case (MENU_ITEM):
            // [ ... Perform menu handler actions ... ]
            return true;

        // 如果未处理菜单项,返回false
        default: return false;
    }
}

如果是在Fragment中创建的菜单项,可以选择在Activity或者Fragment的onOptionsItemSelected处理程序中处理它们。
注意:Activity会首先收到选中的菜单项,如果Activity处理了它并返回true,那么Fragment将不会再收到这个菜单项。

 

子菜单和上下文菜单

子菜单

SubMenu sub=menu.addSubMenu(0,0,Menu.NONE,"SubMenu");
sub.setIcon(android.R.drawable.stat_sys_phone_call);
sub.setHeaderIcon(android.R.drawable.stat_sys_vp_phone_call);

sub.add(0,0,Menu.NONE,"SubMenu Item");

 

上下文菜单和弹出式菜单

上下文菜单是位于当前具有焦点的View的上下文环境中的,当用户长按轨迹球、D-pad的中间按钮或者View(通常是大约3秒)之后触发。
上下文菜单是显示在Activity之上的一个悬浮窗口。

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

        unregisterForContextMenu(findViewById(R.id.scrollView));
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);

        menu.setHeaderTitle("Context Menu");
        menu.add(0,Menu.FIRST,Menu.NONE,"Item 1").setIcon(android.R.drawable.star_on);
        menu.add(0,Menu.FIRST+1,Menu.NONE,"Item 2").setCheckable(true);
        menu.add(0,Menu.FIRST+2,Menu.NONE,"Item 3").setShortcut('2','3');
        SubMenu sub=menu.addSubMenu("SubMenu");
        sub.add("SubMenu Item");
    }

 

弹出式菜单

弹出式菜单是上下文菜单的替代品,它在一个特殊的View实例旁边显示,而不是显示在整个Activity的上方。

当目标运行设备是Android 3.0或者以上版本时,最好使用弹出式菜单而不是上下文菜单。

首先创建XML格式的菜单文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/Menu1" android:title="Popup MenuItem 1"/>
    <item android:id="@+id/Menu2" android:title="Popup MenuItem 2"/>
</menu>
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="New Button"
    android:id="@+id/button"
    android:layout_alignParentTop="true"
    android:layout_alignParentStart="true"
    android:onClick="ShowPopup" />
public void ShowPopup(View view)
{
    PopupMenu popupMenu=new PopupMenu(this,view);
    popupMenu.inflate(R.menu.my_popup_menu);
    popupMenu.show();
}

 

全屏显示

public class viewActivity extends Activity {

    LinearLayout layout;
    RadioButton invisibleBtn;
    RadioButton fullscreenBtn;
    RadioButton navigationBtn;
    RadioButton layoutHideNavigationBtn;
    RadioButton profileBtn;
    RadioButton visibleBtn;
    RadioButton flagsBtn;

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

        layout = (LinearLayout) this.findViewById(R.id.LinearLayout);

        invisibleBtn = (RadioButton) this.findViewById(R.id.INVISIBLE);
        fullscreenBtn = (RadioButton) this.findViewById(R.id.SYSTEM_UI_FLAG_FULLSCREEN);
        navigationBtn = (RadioButton) this.findViewById(R.id.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        layoutHideNavigationBtn = (RadioButton) this.findViewById(R.id.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        profileBtn = (RadioButton) this.findViewById(R.id.SYSTEM_UI_FLAG_LOW_PROFILE);
        visibleBtn = (RadioButton) this.findViewById(R.id.SYSTEM_UI_FLAG_VISIBLE);
        flagsBtn = (RadioButton) this.findViewById(R.id.SYSTEM_UI_LAYOUT_FLAGS);

        invisibleBtn.setOnClickListener(btnClickHandler);
        fullscreenBtn.setOnClickListener(btnClickHandler);
        navigationBtn.setOnClickListener(btnClickHandler);
        layoutHideNavigationBtn.setOnClickListener(btnClickHandler);
        profileBtn.setOnClickListener(btnClickHandler);
        visibleBtn.setOnClickListener(btnClickHandler);
        flagsBtn.setOnClickListener(btnClickHandler);
    }

    private View.OnClickListener btnClickHandler = new View.OnClickListener(){
        public void onClick(View v){
            if (invisibleBtn.isChecked()) {
                layout.setSystemUiVisibility(View.INVISIBLE);
                return;
            }
            if (fullscreenBtn.isChecked()) {
                layout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
                return;
            }
            if (navigationBtn.isChecked()) {
                layout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
                return;
            }
            if (layoutHideNavigationBtn.isChecked()) {
                layout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
                return;
            }
            if (profileBtn.isChecked()) {
                layout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
                return;
            }
            if (visibleBtn.isChecked()) {
                layout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
                return;
            }
            if (flagsBtn.isChecked()) {
                layout.setSystemUiVisibility(View.SYSTEM_UI_LAYOUT_FLAGS);
                return;
            }
        }
    };
}

 

对话框

Dialog

public class MainActivity extends AppCompatActivity {

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

        Button button=(Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Dialog dialog=new Dialog(MainActivity.this);
                dialog.setTitle("Dialog Title");
                dialog.setContentView(R.layout.dialog_view);

                TextView textView=(TextView)dialog.findViewById(R.id.dialog_textView);
                textView.setText("This is the text in my dialog");

                dialog.show();
            }
        });
    }
}

 

AlertDialog

public class MainActivity extends AppCompatActivity {

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

        Button button=(Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Context context=MainActivity.this;

                AlertDialog.Builder ad=new AlertDialog.Builder(context);
                ad.setTitle("It is Pitch Black");
                ad.setMessage("You ar likely to be eaten by a Grue");

                ad.setPositiveButton("Go Back", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });

                ad.setNegativeButton("Move Forward", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });

                ad.setCancelable(true);

                ad.show();
            }
        });
    }
}

使用setCancelable方法来决定用户在没有做任何选择而按下返回按钮时是否应该关闭对话框。

如果选择让对话框可以取消,可使用setOnCancelListener方法附加一个OnCancelListener来响应这个事件。

 

专门输入用的对话框

CharacterPickerDialog

final String options="0123456789ABCDEF";
CharacterPickerDialog characterPickerDialog=new CharacterPickerDialog(MainActivity.this,
        new View(MainActivity.this), null,options,false);
characterPickerDialog.show();

DatePickerDialog

Calendar c = Calendar.getInstance();

DatePickerDialog datePickerDialog=new DatePickerDialog(MainActivity.this
, new DatePickerDialog.OnDateSetListener() {
            @Override
            public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {

        }
    }
,c.get(Calendar.YEAR)
,c.get(Calendar.MONTH)
,c.get(Calendar.DATE));
datePickerDialog.show();

TimePickerDialog

Calendar c = Calendar.getInstance();

TimePickerDialog timePickerDialog=new TimePickerDialog(MainActivity.this,
        new TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

            }
        },c.get(Calendar.HOUR),c.get(Calendar.MINUTE),true);

timePickerDialog.show();

 

Dialog Fragment

DialogFragment就是包含了对话框的Fragment。

public class myDialogFragment extends DialogFragment {
    static String CURRENT_TIME="CURRENT_TIME";

    public static myDialogFragment newInstance(String currentTime){
        myDialogFragment result=new myDialogFragment();
        Bundle bundle=new Bundle();
        bundle.putString(CURRENT_TIME,currentTime);
        result.setArguments(bundle);
        return result;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());

        builder.setTitle("The Current Time Is ...");
        builder.setMessage(getArguments().getString(CURRENT_TIME));

        return builder.create();

    }
}
String tag = "my_dialog";

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String dateString = simpleDateFormat.format(new Date());

DialogFragment myDialogFragment = com.example.guqiang.myapplication.myDialogFragment.newInstance(dateString);

myDialogFragment.show(getFragmentManager(), tag);

也可以通过重写onCreateView处理程序来在DialogFragment中填充一个自定义的对话框布局,就像在自定义Dialog类中所作的一样。

注意:只能选择重写onCreateView和onCreateDialog中的一个。如果重写两者,将导致抛出一个异常。

在提高资源利用率的同时,这种技术还能让Activity在对话框内处理状态信息的保存。当Fragment由于配置变化(如屏幕旋转)而导致重新创见的时候,任何选择或者数据输入都会被持久保存起来。

所以DialogFragment是相对于Dialog来说更好的选择。

 

创建Toast

Toast是短暂出现的通知,它们只会显示几秒钟就消失。Toast不回获取焦点,并且是非模态的,所以它们不会打断当前活动的应用程序。

一般来说,应用程序只有在其中的一个Activity处于激活状态时才可以显示Toast。

Toast toast = Toast.makeText(MainActivity.this, "To health and happiness!", Toast.LENGTH_SHORT);
toast.show();

 

自定义Toast

可以使用setGravity方法把一个Toast放到屏幕的底部。

当文本消息不能满足要求时,可以指定一个自定义的View或者布局来呈现一个更加复杂或者视觉效果更好的界面。

Toast toast = Toast.makeText(MainActivity.this, "Cheers!", Toast.LENGTH_LONG);

LinearLayout linearLayout = new LinearLayout(MainActivity.this);
linearLayout.setOrientation(LinearLayout.VERTICAL);

ImageView imageView = new ImageView(MainActivity.this);
imageView.setImageResource(R.mipmap.ic_launcher);

TextView textView = new TextView(MainActivity.this);
textView.setText("Cheers!");

LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);

linearLayout.addView(textView, layoutParams);
linearLayout.addView(imageView, layoutParams);

toast.setView(linearLayout);

toast.show();

 

Notification

Notification是应用程序提醒用户发生某些事件的一种方式,它无需某个Activity可见。

Notification是那些不可见的应用程序组件(Broadcast Receiver、Service、非活动状态的Activity)的首选机制用来提醒用户,需要他们注意的事件已经发生。他们也可以用来指示持续运行的后台Service - 特别是已经被设置为具有前台优先级的Service。

Notification可以通过不断重复、标记为持续运行或者在状态栏显示一个图标来提醒用户。可以定时地更新状态栏图标,或者使用通知托盘来显示附加信息。

 

Notification Manager

NotificationManager是用来处理Notification的系统Service,使用getSystemService方法可以获取。

NotificationManager notificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

通过使用NotificationManager,可以触发新的Notification,修改现有的Notification,或者删除那些不再需要的Notification。

 

创建Notification

创见Notification和配置状态栏图标

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

int icon = android.R.drawable.star_on;

String tickerText = "Notification";

long when = System.currentTimeMillis();

Notification notification = new Notification(icon, tickerText, when);

notificationManager.notify(0,notification);

默认的声音、闪灯、震动和全部

Notification.DEFAULT_SOUND Notification.DEFAULT_LIGHTS Notification.DEFAULT_VIBRATE Notification.DEFAULT_ALL

notification.defaults = Notification.DEFAULT_ALL;

 

Notification Builder

NotificationBuilder简化了配置Notification的标志、选项、内容和布局的过程,是为较新的Android平台构造Notification的首选方式。

Notification.Builder builder = new Notification.Builder(MainActivity.this);
builder.setSmallIcon(android.R.drawable.star_on);
builder.setTicker("Notification");
builder.setWhen(System.currentTimeMillis());
builder.setDefaults(Notification.DEFAULT_ALL);

Notification notification = builder.getNotification();

 

设置和自定义通知托盘UI

使用标准的Notification UI

Notification.Builder builder = new Notification.Builder(MainActivity.this);
builder.setSmallIcon(android.R.drawable.star_on);
builder.setTicker("Notification");
builder.setContentTitle("Title");
builder.setContentText("Subtitle");
builder.setContentInfo("Info");
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),android.R.drawable.btn_star_big_on));
builder.setWhen(System.currentTimeMillis());
builder.setDefaults(Notification.DEFAULT_ALL);

当用户单击Notification项的时候,会触发它所指定的PendingIntent。在大多数情况下,该Intent应该打开应用程序,并导航到为通知提供了上下文的Activity。

显示进度条-标准

builder.setProgress(100, 50, false);

显示进度条-模糊

builder.setProgress(100, 50, true);

 

创建自定义的Notification UI

如果标准Notification中可用的详细信息不能满足(或不适合)你的要求,那么可以创见自己的布局,并使用Remote Views对象把它分配给Notification。

Notification.Builder builder = new Notification.Builder(MyActivity.this);

//为Notification状态窗口应用一个自定义布局
RemoteViews myRemoteView = new RemoteViews(this.getPackageName(), R.layout.my_notification_layout);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setTicker("Notification");
builder.setWhen(System.currentTimeMillis());
builder.setContentTitle("Progress");
builder.setProgress(100, 50, false);
builder.setContent(myRemoteView);
Notification notification = builder.getNotification();

//自定义扩展的Notification窗口
notification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
notification.contentView.setTextViewText(R.id.status_text, "Current Progress:");
notification.contentView.setProgressBar(R.id.status_progress, 100, 50, false);

//当手动设置contentView属性时,必须同时也设置contentIntent,否则在触发Notification的时候会抛出一个异常。
//为自定义扩展的Notification窗口布局添加单机处理程序
Intent newIntent = new Intent(BUTTON_CLICK_ACTION);
PendingIntent newPendingIntent = PendingIntent.getBroadcast(MyActivity.this, 2, newIntent, 0);
notification.contentView.setOnClickPendingIntent(R.id.status_progress, newPendingIntent);

 

配置持续和连续的Notification

通过设置Notification的FLAG_INSISTENT和FLAG_ONGOING_EVENT标志,可以把它配置为持续的和/或连续的。

标记为“持续的”的Notification可以表示那些当前正在进行的事件(如正在进行的下载或者后台播放的音乐)。

使用Notification Builder的setOnGoing方法,可以将Notification标记为“持续的”。

builder.setSmallIcon(R.drawable.notification_icon)
   .setTicker("Notification")
   .setWhen(System.currentTimeMillis())
   .setContentTitle("Progress")
   .setProgress(100, 50, false)
   .setContent(myRemoteView)
   .setOngoing(true);

如果不使用Notification Builder,可以对Notification的flags属性直接应用FLAG_CONFIG_EVENT标志。

notification.flags = Notification.FLAG_CONFIG_EVENT;

连续的Notification会一直重复音频、振动和闪灯设置,知道被取消位置。FLAG_INSISTENT标志。

由于连续的Notification干扰性很强,因此在第三方应用中应该尽量少使用它。这也是为什么在NotificationBuilder中没有对应的方法来设置这个标志。

 

触发、更新和取消Notification

String svc = Context.NOTIFICATION_SERVICE;

NotificationManager notificationManager = (NotificationManager)getSystemService(svc);

int NOTIFICATION_REF = 1;
Notification notification = builder.getNotification();

notificationManager.notify(NOTIFICATION_REF, notification);

要更新一个已经触发的Notification,包括更新它关联的任何内容View的UI,需要使用Notification Manager重新触发它,并传递给notify方法相同的引用ID。

既可以传入一个相同的Notification对象,也可以传入一个全新的Notification对象。只要ID值是相同的,那么新的Notification就会用来替代状态栏图标以及展开的状态窗口中的内容。

要更新Notification而不触发任何关联的闪灯、振动或者音频,可以使用Notification Builder的setOnlyAlertOnce方法。

要取消一个Notification的操作会把它从状态栏图标中移除,并且从通知托盘中清除它的展开的详细信息。最好当用户操作它后再取消Notification。

通过使用NotificationBuilder的setAutoCancel标志,可以设置Notification在被单击后自动取消自己。

builder.setSmallIcon(R.drawable.ic_launcher)
   .setTicker("Notification")
   .setWhen(System.currentTimeMillis())
   .setContentTitle("Title")
   .setContentText("Subtitle")
   .setContentInfo("Info")
   .setLargeIcon(myIconBitmap)
   .setContentIntent(pendingIntent)
   .setAutoCancel(true);

在没有使用NotificationBuilder的时候,可以应用FLAG_AUTO_CANCEL标志。

要想取消Notification,可以使用NotificationManager的cancel方法,传入要取消的Notification的引用ID。

 

posted @ 2016-10-09 13:49  guqiangjs  阅读(223)  评论(0)    收藏  举报