2017年7月7日,周结(二十一),Activity的构成、Snackbar和TextInputLayout 的使用

来源:互联网 发布:彩虹六号枪械数据 编辑:程序博客网 时间:2024/05/16 08:47
Snackbar 的使用:

使用 Snackbar 要导入 com.android.support:design 库。

Snackbar 显示在所有屏幕其它元素之上(屏幕最顶层),同一时间只能显示一个 snackbar。
Snackbar 的基本使用很简单,与 Toast 类似。
代码如下:

Snackbar.make(view, message_text, duration) .setAction(action_text, click_listener) .show();
make() 方法是生成 Snackbar 的。Snackbar 需要一个控件容器 view 用来容纳,官方推荐使用CoordinatorLayout 来确保 Snackbar 和其他组件的交互,比如滑动取消 Snackbar、Snackbar 出现时 FloatingActionButton 上移。显示时间 duration 有三种类型 LENGTH_SHORT、LENGTH_LONG和 LENGTH_INDEFINITE。setAction() 方法可设置 Snackbar 右侧按钮,增加进行交互事件。如果不使用 setAction() 则只显示左侧 message。
Snackbar.make(linearLayout,"这是massage",Snackbar.LENGTH_LONG).setAction("这是action",newView.OnClickListener() {
@Override
public voidonClick(View v) {
Toast.makeText(MainActivity.this,"你点击了action",Toast.LENGTH_SHORT).show();
}
}).show();

下面这张图演示了点击 Action 弹出 toast 提示。

如果你想在 Snackbar 的显示时或消失时做些什么,可以调用 Snackbar 的 setCallback() 方法。


TextInputLayout 的使用:

它就是一种新的 Layout 布局,并且在这个布局中包含了 EditText 或者其子类控件,这个
布局可以显示一个浮动的文字,用来展示EditText的提示文字 hint 和 EditText 输入错误时的错误提示文字。
setHint():设置 EditText 的提示文字
 setErrorEnabled(boolean):设置是否显示编辑框的错误提示
 setError(CharSequence):设置编辑框的错误提示文字

放一张运行截图:

xml文件代码:
<?xml version="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main_layout"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.administrator.cardviewdemo.MainActivity"><android.support.design.widget.TextInputLayoutandroid:id="@+id/textInputLayout"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="5dp"><EditTextandroid:id="@+id/editText"android:inputType="number"android:padding="10dp"android:layout_width="match_parent"android:layout_height="50dp"/></android.support.design.widget.TextInputLayout><android.support.design.widget.TextInputLayoutandroid:layout_marginTop="30dp"android:id="@+id/textInputLayout2"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="5dp"><EditTextandroid:id="@+id/editText2"android:inputType="textPassword"android:padding="10dp"android:layout_width="match_parent"android:layout_height="50dp"/></android.support.design.widget.TextInputLayout></LinearLayout>


Java文件的代码如下:
packagecom.example.administrator.cardviewdemo;importandroid.os.PersistableBundle;importandroid.support.design.widget.Snackbar;importandroid.support.design.widget.TextInputEditText;importandroid.support.design.widget.TextInputLayout;importandroid.support.v7.app.AppCompatActivity;importandroid.os.Bundle;importandroid.text.Editable;importandroid.text.TextWatcher;importandroid.view.View;importandroid.widget.EditText;importandroid.widget.LinearLayout;importandroid.widget.Toast;public classMainActivityextendsAppCompatActivity {privateTextInputLayouttextInputLayout;privateEditTexteditText;privateTextInputLayouttextInputLayout2;privateEditTexteditText2;privateLinearLayoutlinearLayout;@Overrideprotected voidonCreate(BundlesavedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);linearLayout= (LinearLayout)findViewById(R.id.main_layout);textInputLayout= (TextInputLayout) findViewById(R.id.textInputLayout);textInputLayout2= (TextInputLayout) findViewById(R.id.textInputLayout2);textInputLayout.setHint("请输入4位学号");textInputLayout2.setHint("请输入密码");editText= (EditText) findViewById(R.id.editText);editText2= (EditText) findViewById(R.id.editText2);editText2.addTextChangedListener(newTextWatcher() {@Overridepublic voidbeforeTextChanged(CharSequence s,intstart,intcount,intafter) {}@Overridepublic voidonTextChanged(CharSequence s,intstart,intbefore,intcount) {if(s.length() >4) {textInputLayout2.setError("密码输入错误");textInputLayout2.setErrorEnabled(true);}else{textInputLayout2.setErrorEnabled(false);}}@Overridepublic voidafterTextChanged(Editable s) {}});editText.addTextChangedListener(newTextWatcher() {@Overridepublic voidbeforeTextChanged(CharSequence charSequence,inti,inti1,inti2) {}@Overridepublic voidonTextChanged(CharSequence charSequence,inti,inti1,inti2) {if(charSequence.length() >4) {textInputLayout.setError("学号输入错误");textInputLayout.setErrorEnabled(true);}else{textInputLayout.setErrorEnabled(false);}}@Overridepublic voidafterTextChanged(Editable editable) {}});Snackbar.make(linearLayout,"这是massage", Snackbar.LENGTH_LONG).setAction("这是action",newView.OnClickListener() {@Overridepublic voidonClick(View v) {Toast.makeText(MainActivity.this,"你点击了action",Toast.LENGTH_SHORT).show();}}).show();}}



继承图

视图坐标系
View 获取自身宽高
getHeight():获取 View 自身高度
getWidth():获取 View 自身宽度
View 自身坐标
通过如下方法可以获得 View 到其父控件(ViewGroup)的距离:
getTop():获取 View 自身顶边到其父布局顶边的距离
getLeft():获取 View 自身左边到其父布局左边的距离
getRight():获取 View 自身右边到其父布局左边的距离
getBottom():获取 View 自身底边到其父布局顶边的距离
MotionEvent 提供的方法
我们看上图那个深蓝色的点,假设就是我们触摸的点,我们知道无论是 View 还是 ViewGroup,最终的点击事件都会由 onTouchEvent(MotionEvent event) 方法来处理,MotionEvent 也提供了各种获取焦点坐标的方法:
getX():获取点击事件距离控件左边的距离,即视图坐标
getY():获取点击事件距离控件顶边的距离,即视图坐标
getRawX():获取点击事件距离整个屏幕左边距离,即绝对坐标
getRawY():获取点击事件距离整个屏幕顶边的的距离,即绝对坐标


View 的滑动是 Android 实现自定义控件的基础,同时在开发中我们也难免会遇到 View 的滑动的处理。其实不管是那种滑动的方式基本思想都是类似的:当触摸事件传到 View 时,系统记下触摸点的坐标,手指移动时系统记下移动后的触摸的坐标并算出偏移量,并通过偏移量来修改 View 的坐标。 

使用 Layout 方法使控件滑动。
自定义控件:
packagecom.example.administrator.cardviewdemo;importandroid.content.Context;importandroid.util.AttributeSet;importandroid.view.MotionEvent;importandroid.view.View;/*** Created by Administrator on 2017/7/14.*/public classCustomImageViewextendsView {private intlastX;private intlastY;publicCustomImageView(Context context) {super(context);}publicCustomImageView(Context context, AttributeSet attrs) {super(context, attrs);}publicCustomImageView(Context context, AttributeSet attrs,intdefStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic booleanonTouchEvent(MotionEvent event) {intx = (int) event.getX();inty = (int) event.getY();switch(event.getAction()) {caseMotionEvent.ACTION_DOWN:lastX= x;lastY= y;break;caseMotionEvent.ACTION_MOVE://计算移动的距离intoffsetX = x -lastX;intoffsetY = y -lastY;//调用layout方法来重新放置它的位置layout(getLeft() + offsetX, getTop() + offsetY,getRight() + offsetX, getBottom() + offsetY);break;}return true;}}主页面布局直接引用此控件:<com.example.administrator.cardviewdemo.CustomImageViewandroid:id="@+id/customview"android:layout_width="80dp"android:layout_height="80dp"android:layout_margin="50dp"android:background="@android:color/holo_red_light"/>主页面代码:packagecom.example.administrator.cardviewdemo;importandroid.support.v7.app.AppCompatActivity;importandroid.os.Bundle;public classMainActivityextendsAppCompatActivity {privateCustomImageViewcustomImageView;@Overrideprotected voidonCreate( Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}}



Activity 的构成:

加载 Activity 首先会想到 setcontentview
@Overrideprotected voidonCreate( Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}跟踪一下源码:public voidsetContentView(@LayoutResintlayoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();}...publicWindow getWindow() {returnmWindow;}

从上面看出,里面调用了 mWindow 的 setContentView 方法,尝试追踪一下源码,发现mWindow 是 Window 类型的,但是它是一个抽象类,setContentView 也是抽象方法,所以我们要找到 Window 类的实现类才行。
final voidattach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token,intident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,NonConfigurationInstances lastNonConfigurationInstances,Configuration config, String referrer, IVoiceInteractor voiceInteractor,Window window) {...mWindow=newPhoneWindow(this, window);...}

这里实例化了 PhoneWindow 类,由此得知,PhoneWindow 是 Window 的实现类,那么我们在 PhoneWindow 类里面找到它的 setContentView 方法
@Overridepublic voidsetContentView(intlayoutResID) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if(mContentParent==null) {installDecor();}else if(!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if(hasFeature(FEATURE_CONTENT_TRANSITIONS)) {finalScene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());transitionTo(newScene);}else{mLayoutInflater.inflate(layoutResID,mContentParent);}mContentParent.requestApplyInsets();finalCallback cb = getCallback();if(cb != null&& !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet=true;}

首先判断了 mContentParent 是否为 null,如果为空则执行 installDecor() 方法。可以得知,这个 mContentParent 是我们设置的布局(即 main.xml)的父布局。这个mContentParent 是 mDecor 本身或者是 mDecor 的一个子元素。
这里先梳理一下以上的内容:Activity 通过 PhoneWindow 的 setContentView 方法来设置布局,而设置布局之前,会先判断是否存在 mContentParent,而我们设置的布局文件则是 mContentParent 的子元素。
private voidinstallDecor() {mForceDecorInstall=false;if(mDecor==null) {mDecor= generateDecor(-1);mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);mDecor.setIsRootNamespace(true);if(!mInvalidatePanelMenuPosted&&mInvalidatePanelMenuFeatures!=0) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);}}...if(mContentParent==null) {mContentParent= generateLayout(mDecor);...}}}



调用generateDecor方法,这里实例化了 DecorView,而 DecorView 则是 PhoneWindow类的一个内部类,继承于 FrameLayout,由此可知它也是一个 ViewGroup。
DecorView 是顶级 View,内部有 titlebar 和 contentParent 两个子元素,contentParent 的 id 是 content,而我们设置的 main.xml 布局则是 contentParent 里面的一个子元素。

总结一句话:通过 window 的setContentView方法,创建了 DecorView 和加载了我们提供的布局。


阅读全文
1 0
原创粉丝点击