标签: View

Android 中状态栏、标题栏、View的大小及区分

Android 中状态栏、标题栏、View的大小及区分

1、获得状态栏的高度(状态栏相对Window的位置):

Rect frame = new Rect();

getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);

int statusBarHeight = frame.top;

2、获得mView中显示内容的Top (不包括标题栏,指的是相对Window的位置,即:没有标题栏的时候,指的是状态栏的bottom在Window中的坐标;有标题栏的时候指的是标题栏的bottom在Window中的坐标)

getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

3、获得view中显示内容,与window无关,只和父控件有关

如果布局文件中mView match_parent 那么top=0,bottom=mView.getHeight();

4、获得屏幕的宽高:

(1)

getResources().getDisplayMetrics().heightPixels;

getResources().getDisplayMetrics().widthPixels;

(2)

WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
int width = wm.getDefaultDisplay().getWidth();//屏幕宽度

int height = wm.getDefaultDisplay().getHeight();//屏幕高度

(3)

DisplayMetrics mDisplayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);
int W = mDisplayMetrics.widthPixels;
int H = mDisplayMetrics.heightPixels;

(4)过时方法

Display mDisplay = getWindowManager().getDefaultDisplay();
int W = mDisplay.getWidth();
int H = mDisplay.getHeight();

Android 中状态栏、标题栏、View的大小及区分

Android 中状态栏、标题栏、View的大小及区分

1、获得状态栏的高度(状态栏相对Window的位置):

Rect frame = new Rect();

getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);

int statusBarHeight = frame.top;

2、获得mView中显示内容的Top (不包括标题栏,指的是相对Window的位置,即:没有标题栏的时候,指的是状态栏的bottom在Window中的坐标;有标题栏的时候指的是标题栏的bottom在Window中的坐标)

getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

3、获得view中显示内容,与window无关,只和父控件有关

如果布局文件中mView match_parent 那么top=0,bottom=mView.getHeight();

4、获得屏幕的宽高:

(1)

getResources().getDisplayMetrics().heightPixels;

getResources().getDisplayMetrics().widthPixels;

(2)

WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
int width = wm.getDefaultDisplay().getWidth();//屏幕宽度

int height = wm.getDefaultDisplay().getHeight();//屏幕高度

(3)

DisplayMetrics mDisplayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);
int W = mDisplayMetrics.widthPixels;
int H = mDisplayMetrics.heightPixels;

(4)过时方法

Display mDisplay = getWindowManager().getDefaultDisplay();
int W = mDisplay.getWidth();
int H = mDisplay.getHeight();

IOS 调试技巧 查看View的视图结构

recursiveDescription 是被隐藏的指令 可以在调试阶段查看当前视图的层次结构。也可以检查系统控件构成各个版本之间存在的微小的变化。

下面是我查看UItextField系统控件的构成

%title插图%num

Android实现滑动的几种方式

View的滑动对于View交互性及效果有很大影响,我们可以通过以下四种方式来实现View的滑动,准确地说是View位置的改变。要改变View的位置,首先我们需要了解Android的坐标系,因为View的是通过坐标来定位的。

*对坐标系

Android系统中,屏幕的*左上角为坐标原点,如下图所示。

%title插图%num

屏幕*左上角的点为坐标原点,向右向下分别为x轴和y轴

视图坐标系

视图坐标系是在View的层级体系中使用到的,View的父布局*左上角为坐标原点,向右向下为x轴和y轴,如下图所示:

%title插图%num

几个容易混淆的方法:

getX():视图坐标系点的X坐标
getRawX():*对坐标系点的X坐标
getLeft():视图坐标系View左边框距离ViewGroup左边框距离
getTranslationX():View的偏移量,初始为0,当View发生平移时,其值会变,向右为正,向左为负。
其中view.getX() = view.getLeft() + view.getTranslationX(),而get*Y同理。

1. 通过改变View的布局位置

View的layout方法用来将View放到布局的合适位置,我们可以通过这个方法改变它的left,top,right,bottom参数的值来改变它在布局中的位置。在此基础上,如果我们在用户手指移动的过程中不断地改变View的位置就可以让View跟随手指移动。要实现View跟随用户手指滑动,我们可以监听用户手指的动作(按下,移动,。。。)计算偏移量,通过layout改变View的位置即可。

如下代码则通过layout实现View跟随手指滑动(重写View的onTouchEvent方法)

private int lastX;
private int lastY;

@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,”onTouchEvent() down”);
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,”onTouchEvent() move”);
int offX = x – lastX;
int offY = y -lastY;
layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);
break;
}
return true;
}

这里通过相对坐标计算偏移量完成View的滑动,还可以通过*对坐标计算偏移量,代码如下:

private int lastRawX;
private int lastRawY;

@Override
public boolean onTouchEvent(MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,”onTouchEvent() down”);
lastRawX = rawX;
lastRawY = rawY;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,”onTouchEvent() move”);
int offX = rawX – lastRawX;
int offY = rawY -lastRawY;
layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);
lastRawX = rawX; // 重置坐标
lastRawY = rawY; // 重置坐标
break;
}
return true;
}

与相对坐标不同的是,使用*对坐标需要在move事件结束后重置上一次手指的坐标值,这样才能准确地计算出偏移量。

为什么*对坐标要重置?那是如果不重置的话每次移动都是拿新的坐标与*开始的坐标比较得到偏移量,而*开始的坐标是View的初始位置手指按下的坐标,View每次移动的偏移量应该是新位置的坐标减去上一次的坐标,所以每次移动后需要更新上一次的坐标。

除了使用View的layout方法重新布局View外,还可以使用offsetLeftAndRight和offsetTopAndBottom方法重新布局View,同样可以实现View的滑动效果。代码如下:

private int lastRawX;
private int lastRawY;

@Override
public boolean onTouchEvent(MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,”onTouchEvent() down”);
lastRawX = rawX;
lastRawY = rawY;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,”onTouchEvent() move”);
int offX = rawX – lastRawX;
int offY = rawY -lastRawY;
offsetLeftAndRight(offX);
offsetTopAndBottom(offY);
// layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);
lastRawX = rawX;
lastRawY = rawY;
break;
}
return true;
}

还可以通过View的改变布局参数LayoutParams的leftMargin和topMargin属性值改变View的位置,实现View的滑动,代码如下:

private int lastRawX;
private int lastRawY;

@Override
public boolean onTouchEvent(MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,”onTouchEvent() down”);
lastRawX = rawX;
lastRawY = rawY;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,”onTouchEvent() move”);
int offX = rawX – lastRawX;
int offY = rawY -lastRawY;
// offsetLeftAndRight(offX);
// offsetTopAndBottom(offY);
// layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);
ViewGroup.LayoutParams layoutParams = getLayoutParams();
((ViewGroup.MarginLayoutParams)layoutParams).leftMargin += offX;
((ViewGroup.MarginLayoutParams)layoutParams).topMargin += offY;
setLayoutParams(layoutParams);
lastRawX = rawX;
lastRawY = rawY;
break;
}
return true;
}

改变View的布局参数需要注意的是这个View(或ViewGroup)必须有一个父布局,同时还需要注意布局参数的类型(如LinearLayout.LayoutParams,FrameLayout.LayoutParams),不过可以使用万能的MarginLayoutParams,这样就可以不用考虑布局参数类型了。

2. 使用scrollTo和scrollBy

View类有scrollTo和scollBy方法,它们可以改变View内容的位置,scrollTo表示移动到某个坐标点,scrollBy表示移动多少偏移量。我们可以通过scrollBy实现View跟随手指的滑动,代码如下:

private int lastRawX;
private int lastRawY;

@Override
public boolean onTouchEvent(MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,”onTouchEvent() down”);
lastRawX = rawX;
lastRawY = rawY;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,”onTouchEvent() move”);
int offX = rawX – lastRawX;
int offY = rawY -lastRawY;
// offsetLeftAndRight(offX);
// offsetTopAndBottom(offY);
// layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);
// ViewGroup.LayoutParams layoutParams = getLayoutParams();
// ((ViewGroup.MarginLayoutParams)layoutParams).leftMargin += offX;
// ((ViewGroup.MarginLayoutParams)layoutParams).topMargin += offY;
// setLayoutParams(layoutParams);
((View)getParent()).scrollBy(-offX,-offY);
lastRawX = rawX;
lastRawY = rawY;
break;
}
return true;
}

这里你可能会有所迷惑,为什么调用scrollBy的是View的父View(ViewGroup)?为什么偏移量是负的?

首先scrollBy移动的是View的内容content,而不是View本身,如TextView的content为文本,ImageView的content为drawable,而ViewGroup的content是View或是ViewGroup,所以要移动当前View本身,我们就需要通过它的ViewGroup改变自己的内容从而改变View本身的位置。其次,我们真正操作的是View的父控件ViewGroup,要让View往左(上/右/下)移,应该要让ViewGroup往相反方向移动,也就是右(下/左/上),所以偏移量就是相反的(负的)。下面贴上一张图,感受一下。

%title插图%num

3. 使用Scroller类实现View平滑移动

Android为View的滑动提供了Scroller辅助类,它本身并不能导致View滑动,需要借助computeScroll和ScrollTo方法完成View的滑动。使用Scroller类完成View的平滑,需要通过以下三个步骤:

(1)创建Scroller类

通常在自定义View的构造方法中完成Scroller类的初始化

mScroller = new Scroller(context);
1
(2)重写computeScroll方法

@Override
public void computeScroll() {
super.computeScroll();
// 判断Scroller滑动是否执行完毕
if(mScroller.computeScrollOffset()){
((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
// 通过重绘让系统调用onDraw,onDraw中又会调用computeScroll,如此不断循环,直到Scroller执行完毕
invalidate();
}
}

这里需要注意的是computeScroll方法在onDraw中会被调用,因此需要调用invalidate方法通知View调用onDraw重绘,然后再调用computeScroll完成View的滑动,过程为invalidate->onDraw->computeScroll->invalidate->…,无限循环直到mScroller的computeScrollOffset返回false,也就是滑动完成。

(3)调用Scroller类的startScroll方法开启滚动过程

public void smoothScrollBy(int dx,int dy){
mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),dx,dy,2000);
invalidate(); // 必须调用改方法通知View重绘以便computeScroll方法被调用。
}

接下来就开始模拟滑动过程了,重写onTouchEvent方法,代码如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_UP:
int offX = x – lastX;
int offY = y -lastY;
smoothScrollBy(-offX,-offY);
break;
}
return true;
}

计算偏移量的方法和上面一样,这里实现的效果是手指离开时,View会在2秒内平滑到手指离开时的位置。

4. 使用属性动画实现View的滑动

属性动画可以改变View的属性,那么我们可以通过属性动画改变View的x和y属性从而改变View的位置实现View的滑动,代码如下:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void animationScroll(float dx, float dy){
Path path = new Path();
path.moveTo(dx,dy);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, “x”, “y”, path);
objectAnimator.start();
}

通过执行ObjectAnimator改变x和y属性,我们需要新的x和y属性值,可以通过重写onTouchEvent方法得到新的x和y属性值,代码如下:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_UP:
int offX = x – lastX;
int offY = y -lastY;
animationScroll(getX()+offX,getY()+offY);
break;
}
return true;
}
————————————————

Android实现滑动的几种方式演示

一、前言
*近闲来无事,也不知道研究点什么比较好。就买了几本书,加强基础。这编博客是从徐宜生的Android群英传中总结而来的,非常好的一本书,推荐大家入手。

我将用这几种方式,实现一个可拖动的View。

二、 layout方式
我们都知道View绘制流程的主要三个步骤,onMeaure测量 -onLayout摆放-onDraw绘制。关于这方面的博文太多太多,我也就不再多说。

layout可以控制View在父布局中的摆放位置。
我们只需要监听View的事件,然后一直调用layout方法,让View跟随手指的位移即可。

/**
* Created by AItsuki on 2016/3/1.
*/
public class SlideByLayoutView extends View {

private int startX;
private int startY;

public SlideByLayoutView(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
startX = (int) event.getX();
startY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int endX = (int) event.getX();
int endY = (int) event.getY();
int dx = endX- startX;
int dy = endY – startY;
layout(getLeft() + dx, getTop() + dy, getRight() + dx, getBottom() + dy);
break;
}
return true;
}
}

%title插图%num
三、scrollTo和scrollBy
scrollTo(x,y):移动到具体位置
scrollBy(dx,dy):移动增量,相对于自身位置。
将二中的layout(getLeft() + dx, getTop() + dy, getRight() + dx, getBottom() + dy);替换成scrollBy(dx,dy)
会发现不能拖动方块,因为scroll移动的是View的内容,而不是自身。所以在使用scroll的时候,我们应该移动父布局的内容。

/**
* Created by AItsuki on 2016/3/1.
*/
public class SlideByScroll extends View {

private int startX;
private int startY;

public SlideByScroll(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int endX = (int) event.getRawX();
int endY = (int) event.getRawY();
int dx = endX – startX;
int dy = endY – startY;
// layout(getLeft() + dx, getTop() + dy, getRight() + dx, getBottom() + dy);
View parent = (View) getParent();
parent.scrollBy(dx, dy);

startX = endX;
startY = endY;
break;
}
return true;
}
}

注意这里不能使用getX(), 而需要获取getRawX()。因为getX是获取View自身内部的坐标,我们需要移动的是父布局,所以我们应该获取屏幕的坐标。

然后我们再次运行会发现,方块居然是反方向运行的。

 

%title插图%num

 

但是,为什么会这样呢?
因为scrollBy滚动的手机屏幕,看下面这张图给就可以很好的理解了(黑框请看作手机屏幕)。

%title插图%num

解决方式就是:在dx和dy前面加个负号就行了,parent.scrollBy(-dx, -dy);

3.1 Scroller
既然说到了scrollBy和scrollTo,那么这里就不能说一下scroller了。

如果我想实现这么个效果,当我点击方块的时候,让他平滑滚动一段距离,我们应该怎么做呢?
google提供了一个很方便的东西,那就是scroller。虽然属性动画也可以,但是我们现在就来说说scroller。

scroller是什么?
scroller可以模拟一个滚动过程,它有两个方法开启模拟效果。

public void startScroll(int startX, int startY, int dx, int dy)
public void startScroll(int startX, int startY, int dx, int dy, int duration)

其实scroller并没有作用于View,它只是模拟了一个过程而已。
实际滚动其实还需要我们自己调用scrollTo方法。

/**
* Created by AItsuki on 2016/3/1.
*/
public class ScrollerDemo extends View implements View.OnClickListener {

private final Scroller mScroller;

public ScrollerDemo(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
setOnClickListener(this);
}

// computerScroll方法会在invalidate执行的时候调用。
@Override
public void computeScroll() {
super.computeScroll();
// 判断scroller是否执行完毕
if(mScroller.computeScrollOffset()) {
((View)getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}

@Override
public void onClick(View v) {
View parent = (View) getParent();
mScroller.startScroll(parent.getScrollX(),parent.getScrollY(),-100,-100);
invalidate();
}
}

%title插图%num

四、属性动画
/**
* Created by AItsuki on 2016/3/2.
*/
public class SlideByAnimator extends View {

private float startX;
private float startY;

public SlideByAnimator(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float endX = event.getRawX();
float endY = event.getRawY();
float dx = endX – startX;
float dy = endY – startY;

ObjectAnimator.ofFloat(this, “translationX”, getTranslationX() + dx).setDuration(0).start();
ObjectAnimator.ofFloat(this, “translationY”, getTranslationY() + dy).setDuration(0).start();

startX = endX;
startY = endY;
break;
}
return true;
}

}

虽然属性动画会改变控件的位置,并且能获取到点击事件。
但是,实际上View的位置并没有改变,通过getLeft(),getTop()等方法获取的值也未曾改变。
属性动画会把你位移过的距离保存起来,所以可以通过getleft()+getTranslationX()获取到当前View显示的准确位置。

五、ViewDragHelper
ViewDragHelper是一个非常强大的类,可以帮我们处理复杂的拖动逻辑。
Android自带的侧边栏就用到了这个类,在这里我们也用它实现一个简单的侧边栏。

<?xml version=”1.0″ encoding=”utf-8″?>
<com.aitsuki.slidedemo.SimpleDrawerLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
tools:context=”.MainActivity”>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background=”#000″>

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:text=”侧边栏”
android:textColor=”#fff”
android:textSize=”30dp” />

</LinearLayout>

<FrameLayout
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background=”#fff”>

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center”
android:text=”主页面”
android:textColor=”#000″
android:textSize=”30dp” />

</FrameLayout>
</com.aitsuki.slidedemo.SimpleDrawerLayout>

 

/**
* Created by AItsuki on 2016/3/3.
*/
public class SimpleDrawerLayout extends FrameLayout {

private final ViewDragHelper mDragHelper;
private View mContent;
private int mMenuWidth;

public SimpleDrawerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mDragHelper = ViewDragHelper.create(this, mCallBack);
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();
mContent = getChildAt(1);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mMenuWidth = w / 3 * 2;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragHelper.shouldInterceptTouchEvent(ev);
}

private ViewDragHelper.Callback mCallBack = new ViewDragHelper.Callback() {

// 触摸到View的时候就会回调这个方法。
// return true表示抓取这个View。
@Override
public boolean tryCaptureView(View child, int pointerId) {
return mContent == child;
}

@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {

return left > 0 ? left > mMenuWidth ? mMenuWidth : left : 0; // 只能右划出菜单,并且菜单*大宽度为屏幕3分之2
}

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);

if (xvel > 300) {
open();
} else if (xvel < -300) {
close();
} else {
if (mContent.getLeft() > mMenuWidth / 2) {
open();
} else {
close();
}
}
}
};

private void close() {
mDragHelper.smoothSlideViewTo(mContent, 0, 0);
invalidate();
}

private void open() {
mDragHelper.smoothSlideViewTo(mContent, mMenuWidth, 0);
invalidate();
}

@Override
public void computeScroll() {
super.computeScroll();
if (mDragHelper.continueSettling(true)) {
invalidate();
}
}
}
%title插图%num

————————————————

Android面试题View篇

Android面试题View篇,由本人整理汇总,后续将推出系列篇,如果喜欢请持续关注和推荐。

Activity生命周期?

onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()

Activity的启动过程(不要回答生命周期)

app启动的过程有两种情况,*种是从桌面launcher上点击相应的应用图标,第二种是在activity中通过调用startActivity来启动一个新的activity。 我们创建一个新的项目,默认的根activity都是MainActivity,而所有的activity都是保存在堆栈中的,我们启动一个新的activity就会放在上一个activity上面,而我们从桌面点击应用图标的时候,由于launcher本身也是一个应用,当我们点击图标的时候,系统就会调用startActivitySately(),一般情况下,我们所启动的activity的相关信息都会保存在intent中,比如action,category等等。我们在安装这个应用的时候,系统也会启动一个PackaManagerService的管理服务,这个管理服务会对AndroidManifest.xml文件进行解析,从而得到应用程序中的相关信息,比如service,activity,Broadcast等等,然后获得相关组件的信息。当我们点击应用图标的时候,就会调用startActivitySately()方法,而这个方法内部则是调用startActivty(),而startActivity()方法*终还是会调用startActivityForResult()这个方法。而在startActivityForResult()这个方法。因为startActivityForResult()方法是有返回结果的,所以系统就直接给一个-1,就表示不需要结果返回了。而startActivityForResult()这个方法实际是通过Instrumentation类中的execStartActivity()方法来启动activity,Instrumentation这个类主要作用就是监控程序和系统之间的交互。而在这个execStartActivity()方法中会获取ActivityManagerService的代理对象,通过这个代理对象进行启动activity。启动会就会调用一个checkStartActivityResult()方法,如果说没有在配置清单中配置有这个组件,就会在这个方法中抛出异常了。当然*后是调用的是Application.scheduleLaunchActivity()进行启动activity,而这个方法中通过获取得到一个ActivityClientRecord对象,而这个ActivityClientRecord通过handler来进行消息的发送,系统内部会将每一个activity组件使用ActivityClientRecord对象来进行描述,而ActivityClientRecord对象中保存有一个LoaderApk对象,通过这个对象调用handleLaunchActivity来启动activity组件,而页面的生命周期方法也就是在这个方法中进行调用。

Activity的启动模式

standard:默认标准模式,每启动一个都会创建一个实例,

singleTop:栈顶复用,如果在栈顶就调用onNewIntent复用,从onResume()开始

singleTask:栈内复用,本栈内只要用该类型Activity就会将其顶部的activity出栈

singleInstance:单例模式,除了3中特性,系统会单独给该Activity创建一个栈,

Activity缓存方法

1.配置改变导致Activity被杀死,横屏变竖屏:在onStop之前会调用onSaveInstanceState()保存数据在重建Activity之后,会在onStart()之后调用onRestoreInstanceState(),并把保存下来的Bundle传给onCreate()和它会默认重建Activity当前的视图,我们可以在onCreate()中,回复自己的数据。

2.内存不足杀掉Activity,优先级分别是:前台可见,可见非前台,后台。

Service的生命周期,两种启动方法,有什么区别

context.startService() ->onCreate()- >onStart()->Service running–>(如果调用context.stopService() )->onDestroy() ->Service shut down

1.如果Service还没有运行,则调用onCreate()然后调用onStart();

2.如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。

3.调用stopService的时候直接onDestroy,

4.如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service。

context.bindService()->onCreate()->onBind()->Service running–>onUnbind() -> onDestroy() ->Service stop

1.onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。

2.这个时候会把调用者和Service绑定在一起,Context退出了,Service就会调用onUnbind->onDestroy相应退出。

3.所以调用bindService的生命周期为:onCreate –> onBind(只一次,不可多次绑定) –> onUnbind –> onDestory。

静态的Broadcast 和动态的有什么区别

1.动态的比静态的安全

2.静态在app启动的时候就初始化了 动态使用代码初始化

3.静态需要配置 动态不需要

4.生存期,静态广播的生存期可以比动态广播的长很多

5.优先级动态广播的优先级比静态广播高

6.静态不受页面生命周期的影响,即使退出了页面,也可以收到广播这种广播一般用于想开机自启动啊等等,由于这种注册的方式的广播是常驻型广播,所以会占用CPU的资源。

7.动态叫非常驻型广播,收到生命周期的影响,退出页面后,就不会收到广播,我们通常运用在更新UI方面。这种注册方式优先级较高。*后需要解绑,否会会内存泄露。

8.广播是分为有序广播和无序广播。

Android的布局方式有哪些?

LinearLayout,RelativeLayout,TableLayout,FrameLayout,AbsoluteLayout,GridLayout

在创建fragment时如何传递初始化参数?

Fragment初始化一定要提供默认构造函数。不能用构造函数传递参数!不要写带参数的构造函数。在Fragment里添加获取Fragment的newInstance函数,以后获取Fragment就使用这个函数,不要使用构造函数新建Fragment!使用setArgument和getArgument传递参数

设备横竖屏切换的时候,接下来会发生什么?

1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

2、设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

3、设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

Android启动Service的两种方式是什么? 它们的适用情况是什么?

如果后台服务开始后基本可以独立运行的话,可以用startService。音乐播放器就可以这样用。它们会一直运行直到你调用 stopSelf或者stopService。你可以通过发送Intent或者接收Intent来与正在运行的后台服务通信,但大部分时间,你只是启动服务并让它独立运行。如果你需要与后台服务通过一个持续的连接来比较频繁地通信,建议使用bind()。比如你需要定位服务不停地把更新后的地理位置传给UI。Binder比Intent开发起来复杂一些,但如果真的需要,你也只能使用它。

startService:生命周期与调用者不同。启动后若调用者未调用stopService而直接退出,Service仍会运行

bindService:生命周期与调用者绑定,调用者一旦退出,Service就会调用unBind->onDestroy

谈谈你对Android中Context的理解?

Context是一个抽象基类。在翻译为上下文,也可以理解为环境,是提供一些程序的运行环境基础信息。Context下有两个子类,ContextWrapper是上下文功能的封装类,而ContextImpl则是上下文功能的实现类。而ContextWrapper又有三个直接的子类, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一个带主题的封装类,而它有一个直接子类就是Activity,所以Activity和Service以及Application的Context是不一样的,只有Activity需要主题,Service不需要主题。Context一共有三种类型,分别是Application、Activity和Service。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的,因此在*大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

getApplicationContext()和getApplication()方法得到的对象都是同一个application对象,只是对象的类型不一样。 Context数量 = Activity数量 + Service数量 + 1 (1为Application)

理解Activity,View,Window三者关系

这个问题真的很不好回答。所以这里先来个算是比较恰当的比喻来形容下它们的关系吧。Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)LayoutInflater像剪刀,Xml配置像窗花图纸。 1:Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。 2:这个PhoneWindow有一个“ViewRoot”,这个“ViewRoot”是一个View或者说ViewGroup,是*初始的根视图。 3:“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等 4:这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等。

Service的onCreate回调在UI线程中吗?

Service生命周期的各个回调和其他的应用组件一样,是跑在主线程中,会影响到你的UI操作或者阻塞主线程中的其他事情。

View的绘制流程

自定义控件: 1、组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏。 2、继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。如制作圆角,圆形图片。 3、完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如说制作水波纹进度条。

View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()

*步:OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。

第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。

第三步:OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用; ⑤、还原图层(Layer);⑥、绘制滚动条。

View,ViewGroup事件分发

Touch事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。

ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。

触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。

当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

Android中touch事件的传递机制是怎样的?

1.Touch事件传递的相关API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent

2.Touch事件相关的类有View、ViewGroup、Activity

3.Touch事件会被封装成MotionEvent对象,该对象封装了手势按下、移动、松开等动作

4.Touch事件通常从Activity#dispatchTouchEvent发出,只要没有被消费,会一直往下传递,到*底层的View

5.如果Touch事件传递到的每个View都不消费事件,那么Touch事件会反向向上传递,*终交由Activity#onTouchEvent处理

6.onInterceptTouchEvent为ViewGroup特有,可以拦截事件

7.Down事件到来时,如果一个View没有消费该事件,那么后续的MOVE/UP事件都不会再给它

Fragment与Fragment、Activity通信的方式

直接在一个Fragment中调用另外一个Fragment中的方法

使用接口回调

使用广播

Fragment直接调用Activity中的public方法

在创建fragment时如何传递初始化参数?

Fragment初始化一定要提供默认构造函数。不能用构造函数传递参数!不要写带参数的构造函数。在Fragment里添加获取Fragment的newInstance函数,以后获取Fragment就使用这个函数,不要使用构造函数新建Fragment!使用setArgument和getArgument传递参数

如何规避oom?

使用更加轻量的数据结构

避免在Android里面使用Enum

减小Bitmap对象的内存占用

使用更小的图片

复用系统自带的资源

注意在ListView/GridView等出现大量重复子组件的图里面对ConvertView的复用

Bitmap对象的复用

避免在onDraw方法里面执行对象的创建

避免对象的内存泄露(重点)

考虑使用Application Context而不是Activity Context

注意WebView的泄漏(重点)

资源文件需要选择合适的文件夹进行存放

谨慎使用static对象(重点)

特别留意单例对象中不合理的持有

珍惜Services资源

谨慎使用“抽象”编程

谨慎使用依赖注入框架

.谨慎使用多进程

Handler的使用(重点)

强软弱虚引用的应用(重点)

主线程操作UI,子线程操作数据(必填)

Android 中如何捕获未捕获的异常

自 定 义 一 个 Application , 比 如 叫 MyApplication 继 承 Application 实 现 UncaughtExceptionHandler。 覆写 UncaughtExceptionHandler 的 onCreate 和 uncaughtException 方法。

保存Activity状态

onSaveInstanceState(Bundle)会在activity转入后台状态之前被调用,也就是onStop()方法之前,onPause方法之后被调用;

数据存储有哪些方式?

1.sharedpreferences 2.file 3.Sqlite 4.ContentProvide 5.网络存储

如何将一个Activity设置成窗口的样式?

*种方法,在styles.xml文件中,可以新建如下的类似Dialog的style。

<style name=”Theme.FloatActivity” parent=”android:style/Theme.Dialog”> </style>。
第二种方法,在AndroidManifest.xml中在需要显示为窗口的Activity中添加如下属性: android:theme=“@style/Theme.FloatActivity”。 也可以直接添加对应需要展示为Dialog style的Activity的android:theme属性为android:theme=“@android:style/Theme.Dialog”。

ScrollView是否可以和ListView混合使用?如何可以,说明混合使用的方式,如果不行,说明原因。

可以,计算整个ListView的高度,填充数据后重新设置ListView高度,重写onMeasure和onInterceptTouchEvent方法

解决ScrollView嵌套ListView和GridView冲突的方法

重写ListView的onMeasure方法,来自定义高度: 解决ScrollView嵌套ListView和GridView冲突的方法 重写ListView的onMeasure方法,来自定义高度:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
通过Intent传递一些二进制数据的方法有哪些? 使用Serializable接口实现序列化,这是Java常用的方法。 实现Parcelable接口,这里Android的部分类比如Bitmap类就已经实现了,同时Parcelable在Android AIDL中交换数据也很常见的。

Serializable 和 Parcelable 的区别

在使用内存的时候,Parcelable 类比 Serializable 性能高,所以推荐使用 Parcelable 类。

Serializable 在序列化的时候会产生大量的临时变量,从而引起频繁的 GC。

Parcelable 不能使用在要将数据存储在磁盘上的情况。尽管 Serializable 效率低点,但在这 种情况下,还是建议你用 Serializable 。

Bitmap的处理

1.当使用ImageView的时候,可能图片的像素大于ImageView,此时就可以通过BitmapFactory.Option来对图片进行压缩,inSampleSize表示缩小2^(inSampleSize-1)倍。

2.BitMap的缓存:

1.同步加载只创建一个线程然后按照顺序进行图片加载

2.异步加载使用线程池,让存在的加载任务都处于不同线程

3.为了不开启过多的异步任务,只在列表静止的时候开启图片加载

1.使用LruCache进行内存缓存。

2.使用DiskLruCache进行硬盘缓存。

3.实现一个ImageLoader的流程:同步异步加载、图片压缩、内存硬盘缓存、网络拉取

过度绘制、卡顿优化:

1.过度绘制:

1.移除Window默认的Background:getWidow.setBackgroundDrawable(null);

2.移除XML布局文件中非必需的Background

3.减少布局嵌套(扁平化的一个体现,减少View数的深度,也就减少了View树的遍历时间,渲染的时候,前后期的工作,总是按View树结点来)

4.在引入布局文件里面,*外层可以用merge替代LinearLayout,RelativeLayout,这样把子UI元素直接衔接在include位置

5.工具:HierarchyViewer 查看视图层级

2.卡顿优化:16ms数据更新

view绘制机制和加载过程,请详细说整个流程

1.ViewRootImpl会调用performTraversals(),其内部会调用performMeasure()、performLayout、performDraw()。

2.performMeasure()会调用*外层的ViewGroup的measure()–>onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),这之中会遍历子View然后循环调用measureChild()这之中会用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起获取本View的MeasureSpec,然后调用子View的measure()到View的onMeasure()–>setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默认返回measureSpec的测量数值,所以继承View进行自定义的wrap_content需要重写。

3.performLayout()会调用*外层的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()设置本View的四个顶点位置。在onLayout(抽象方法)中确定子View的位置,如LinearLayout会遍历子View,循环调用setChildFrame()–>子View.layout()。

4.performDraw()会调用*外层ViewGroup的draw():其中会先后调用background.draw()(绘制背景)、onDraw()(绘制自己)、dispatchDraw()(绘制子View)、onDrawScrollBars()(绘制装饰)。

5.MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(对应精确值和match_parent)、AT_MOST(对应warp_content))和30位SpecSize组成一个int,DecorView的MeasureSpec由窗口大小和其LayoutParams决定,其他View由父View的MeasureSpec和本View的LayoutParams决定。ViewGroup中有getChildMeasureSpec()来获取子View的MeasureSpec。

6.三种方式获取measure()后的宽高:

1.Activity#onWindowFocusChange()中调用获取

2.view.post(Runnable)将获取的代码投递到消息队列的尾部。

3.ViewTreeObservable.

Bitmap图像模式有哪几种,给出一张1080 * 1920的,ARGB 8888格式的占用内存是多大

Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位

Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。

ARGB:指的是一种色彩模式,里面A代表Alpha,R表示red,G表示green,B表示blue。 ARGB 8888一个像素占用4个字节,一个字节8位,1080 * 1920 * 4 * 8

图片优化

对图片本身进行操作。尽量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource来设置一张大图,因为这些方法在完成decode后, *终都是通过java层的createBitmap来完成的,需要消耗更多内存.

图片进行缩放的比例,SDK中建议其值是2的指数值,值越大会导致图片不清晰。

不用的图片记得调用图片的recycle()方法

Android UI适配

字体使用sp,使用dp,多使用match_parent,wrap_content,weight 图片资源,不同图片的的分辨率,放在相应的文件夹下可使用百分比代替。

Android中的几种动画

帧动画:Drawable Animation,指通过指定每一帧的图片和播放时间,有序的进行播放而形成动画效果,比如想听的律动条。

补间动画:View Animation(Tween Animation),指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。注意:只是在视图层实现了动画效果,并没有真正改变View的属性,比如滑动列表,改变标题栏的透明度。

属性动画:Property Animation,在Android3.0的时候才支持,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。比如view的旋转,放大,缩小。

HybridApp WebView和JS交互

Android与JS通过WebView互相调用方法,实际上是: Android去调用JS的代码

通过WebView的loadUrl(),使用该方法比较简洁,方便。但是效率比较低,获取返回值比较困难。

通过WebView的evaluateJavascript(),该方法效率高,但是4.4以上的版本才支持,4.4以下版本不支持。所以建议两者混合使用。 JS去调用Android的代码

通过WebView的addJavascriptInterface()进行对象映射 ,该方法使用简单,仅将Android对象和JS对象映射即可,但是存在比较大的漏洞。

漏洞产生原因是:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。 解决方式: (1)Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击。 (2)在Android 4.2版本之前采用拦截prompt()进行漏洞修复。

本文完~

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速