(二)用户交互
2.1ActionBar
1.首先,调用onCreateOptionsMenu(Menu menu)方法为actionbar设置样式
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.support,menu); return true; }
res/menu/support.xml如下
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_delete" android:icon="@android:drawable/ic_menu_delete" app:showAsAction="ifRoom" android:title="delete" /> <item android:id="@+id/action_setting" android:showAsAction="never" android:orderInCategory="100" android:title="action_setting" /> </menu>
接着,若Activity继承自AppCompatActivity,则调用代码
android.support.v7.app.ActionBar actionBar=getSupportActionBar();
否则直接调用
ActionBar actionBar=getActionBar();
2.2ToolBar
如今Google官方推荐使用Toolbar替代ActionBar
1.首先在XML中定义资源,要注意主题颜色,因为Toolbar默认不会自动继承当前material design的风格
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorAccent" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
接着在代码中将toolbar集成
Toolbar toolbar= (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar);
2.3动态方向锁定
1.通过setRequestedOrientation()方法实现,首先设置一个tooglebutton来设置是否锁定
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_orientation); toggleButton = (ToggleButton) findViewById(R.id.tb_orientation); assert toggleButton != null; if(getRequestedOrientation()!= ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED){ toggleButton.setChecked(true); }else{ toggleButton.setChecked(false); } toggleButton.setOnCheckedChangeListener(this); }
接着在监听器中对屏幕方向进行动态的设置
@Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { int current=getResources().getConfiguration().orientation; if(isChecked){ switch (current){ case Configuration.ORIENTATION_LANDSCAPE: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; case Configuration.ORIENTATION_PORTRAIT: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); break; default: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } }else{ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } }
2.5创建上下文动作
1.对于单个项相关上下文,使用PopupMenu
首先自定义ContextListItem类以封装PopupMenu
public class ContextListItem extends LinearLayout implements PopupMenu.OnMenuItemClickListener, View.OnClickListener { private PopupMenu mPopupMenu; private TextView mTextView; public ContextListItem(Context context) { this(context,null); } public ContextListItem(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ContextListItem(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onMenuItemClick(MenuItem item) { String string=mTextView.getText().toString(); switch (item.getItemId()){ case R.id.action_delete: Toast.makeText(getContext(), "delete"+string,Toast.LENGTH_SHORT).show(); break; case R.id.action_setting: Toast.makeText(getContext(), "edit"+string,Toast.LENGTH_SHORT).show(); break; } return true; } @Override protected void onFinishInflate() { super.onFinishInflate(); mTextView= (TextView) findViewById(R.id.text); View contextButton=findViewById(R.id.context); contextButton.setOnClickListener(this); mPopupMenu=new PopupMenu(getContext(),contextButton); mPopupMenu.setOnMenuItemClickListener(this); mPopupMenu.inflate(R.menu.support); }
其中,list_item.xml如下
<?xml version="1.0" encoding="utf-8"?> <com.vastsum.joshua.android50.day20160927.ContextListItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeightSmall" android:orientation="horizontal" android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" android:paddingRight="?android:attr/listPreferredItemPaddingRight" android:background="?android:attr/activatedBackgroundIndicator" > <TextView android:id="@+id/text" android:layout_width="0dp" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceListItemSmall" android:layout_weight="1" android:layout_gravity="center_vertical"/> <ImageView android:id="@+id/context" style="@style/Widget.AppCompat.Light.ActionButton.Overflow" android:layout_width="?android:attr/listPreferredItemHeightSmall" android:layout_height="match_parent" android:clickable="true" android:focusable="false" /> </com.vastsum.joshua.android50.day20160927.ContextListItem>
2.如果会影响多个项,则可以启用ActionMode来响应用户动作,下面展示二者相结合的完整代码
public class ActionActivity extends AppCompatActivity implements AbsListView.MultiChoiceModeListener { private static final String[] ITEMS = {"MOM", "DAD", "MOM", "DAD", "MOM", "DAD", "MOM", "DAD", "MOM", "DAD"}; private ListView mList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mList = new ListView(this); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.text, ITEMS); mList.setAdapter(adapter); mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); mList.setMultiChoiceModeListener(this); setContentView(mList, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { startActionMode(ActionActivity.this); } }); } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { int count = mList.getCheckedItemCount(); mode.setTitle(String.format("%d Select", count)); } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.support, menu); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return true; } @Override public void onDestroyActionMode(ActionMode mode) { } }
2.8自定义BACK键
1.之前实现返回功能都是直接调用activity的finish()方法,现在发现可以调用
super.onBackPressed()
2.在默认情况下,UI中添加Fragment的操作不会再任务的回退栈中添加相应的Fragment,因此back键不能回退。
因此,首先要通过FragmentTransaction的addToBackStack()方法将fragment添加到回退栈
ft=getFragmentManager().beginTransaction(); ft.add(R.id.fl, MyFragment.newInstance("Second")); ft.addToBackStack("Second"); ft.commit();
接着调用FragmentTransaction的popBackStack()方法将fragment弹出
public void back(View view) { getFragmentManager().popBackStack(); }
顺便复习一下fragment的用法
public static class MyFragment extends Fragment{ private String title; public static MyFragment newInstance(String title){ MyFragment fragment= new MyFragment(); fragment.setTitle(title); return fragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { TextView textView=new TextView(getActivity()); textView.setText(title); return textView; } public void setTitle(String title) { this.title = title; } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_back); ft = getFragmentManager().beginTransaction(); ft.add(R.id.fl, MyFragment.newInstance("First")); ft.commit(); }
2.10监控TextView的变动
1.创建一个Watcher实现TextWatcher接口
public class CurrencyTextWatcher implements TextWatcher { boolean mEditing; public CurrencyTextWatcher() { mEditing = false; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public synchronized void afterTextChanged(Editable s) { if (!mEditing) { mEditing = true; String digits = s.toString().replaceAll("\\D", ""); NumberFormat nf = NumberFormat.getCurrencyInstance(); try { String formatted = nf.format(Double.parseDouble(digits) / 100); s.replace(0, s.length(), formatted); } catch (NumberFormatException n) { s.clear(); } mEditing = false; } } }
2.在Activity中设置Watcher监听
editText.addTextChangedListener(new CurrencyTextWatcher());
2.12消除软键盘
1.在特定的用户信息录入界面很有用处
public void hide(View view) { InputMethodManager imm= (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(view.getWindowToken(),0); }
2.13处理复杂的触摸事件
talk is cheap ,show you the code!
1.实现自定义的一个scrollview
public class PanGestureScrollView extends FrameLayout { private GestureDetector mDetector; private Scroller mScroller; private float mInitialX, mInitialY; private int mTouchSlop; public PanGestureScrollView(Context context) { this(context, null); } public PanGestureScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PanGestureScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mDetector = new GestureDetector(context, mListener); mScroller = new Scroller(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { int childWidthMeasureSpec; int childHeightMeasureSpec; childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } @Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED); final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } private GestureDetector.SimpleOnGestureListener mListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { if (!mScroller.isFinished()) { mScroller.abortAnimation(); } return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { fling((int) -velocityX / 3, (int) -velocityY / 3); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { scrollBy((int) distanceX, (int) distanceY); return true; } }; private void fling(int i, int i1) { if (getChildCount() > 0) { int height = getHeight() - getPaddingBottom() - getPaddingTop(); int width = getWidth() - getPaddingLeft() - getPaddingRight(); int bottom = getChildAt(0).getHeight(); int right = getChildAt(0).getWidth(); mScroller.fling(getScrollX(), getScrollY(), i, i1, 0, Math.max(0, right - width), 0, Math.max(0, bottom - height)); invalidate(); } } private int clamp(int n, int my, int child) { if (my >= child || n < 0) { return 0; } if ((my + n) > child) { return child - my; } return n; } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { int oldX = getScrollX(); int oldY = getScrollY(); int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); if (getChildCount() > 0) { View child = getChildAt(0); x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()); y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight()); if (x != oldX || y != oldY) { scrollTo(x, y); } } } postInvalidate(); } @Override public void scrollTo(int x, int y) { if(getChildCount()>0){ View child=getChildAt(0); x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()); y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight()); if(x!=getScrollX()||y!=getScrollY()){ super.scrollTo(x,y); } } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: mInitialX=ev.getX(); mInitialY=ev.getY(); mDetector.onTouchEvent(ev); break; case MotionEvent.ACTION_MOVE: final float x=ev.getX(); final float y=ev.getY(); final int yDiff= (int) Math.abs(y-mInitialY); final int xDiff= (int) Math.abs(x-mInitialX); if(yDiff>mTouchSlop||xDiff>mTouchSlop){ return true; } break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { return mDetector.onTouchEvent(event); } }