自定义view
资料
自定义分为俩种,一种是组合类型的自定义view,还有一种是继承自View或者ViewGroup的自定义View,组合类型的自定义View 比较简单,仅仅需要将公用的内容合并为一个控件(如 :列表展示),好处是封装了控件,可以不用重复写xml文件,配置起来也方便。
用到的相关属性 :
- xml配置
- style 文件配置
<declare-styleable name="MyView">
<attr name="width_size" format="dimension"></attr>
</declare-styleable>
- 自定义view中获取属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
size = typedArray.getDimensionPixelSize(R.styleable.MyView_width_size,30);
typedArray.recycle();//注意回收
- 组合控件的定义
这种自定义控件继承自View或者ViewGroup,
首先需要默认重写onLayout()方法(继承自ViewGroup的view自定重写onLayout方法),需要重写onMeasure()方法,有可能需要重写onDraw()方法。整个自定义View的绘制流程是onMeasure()->onLayout()->onDraw(),
onMeasure方法中需要注意方法的参数 widthMeasureSpec,heightMeasureSpec都是二进制表示,都是32位的,前面2位是测量的模式,后面30位是测量的值,
3种测量模式:分别是
MeasureSpec.AT_MOST,MeasureSpect,EXACTLY,UNSPECiFiED - wrap_content-> MeasureSpec.AT_MOST
- match_parent->MeasureSpec.EXACTLY
- 具体的值 ->MeaseureSpec.EXACTLY
如果测量模式是MeasureSpec.EXACTLY ,这个时候系统已经检测出具体的大小了,对应的是Layoutparams的match_parent或者具体的值,如果测量模式MeasureSpec.AT_MOST,父容器指定一个可用大小,View的大小不能大于这个值对应LayoutParams 的wrap_content,那么我们需要计算控件的宽高,然后通过setMeasuredDimension()重新设置空间的宽高。UNSPECiFIFE是父容器不对View限制,要多大给多大,这种一般用于系统内部,是一种测量状态。
onLayout()方法确定控件的位置,是自上而下的,先确定子布局的位置,然后再确定自己本身的位置,子布局通过layout方法,即可显示到指定的位置。
onDraw()有俩种 dispacthDraw()和onDraw(), dispatchDraw()绘制子控件,onDraw()方法是绘制控件本身,调用顺序是onDraw()->dispatchDraw()
在绘制view控件是需要重写onDraw()方法,在绘制ViewGroup()方法时,需要重写dispatchDraw()方法。
方法绘制当前的控件,首先绘制当前的背景,背景绘制完成之后,再绘制自己,再绘制子view,再绘制装饰,通过调用postInvalidate()或者invalidate()方法刷新页面。
postInvalidate()是在非主线程调用,invalidate()是在主线程调用,差别是渲染的时效性方面有影响。
注意:getMeasuredWidth()与getWidth()的区别。他们的值大部分时间都是相同的,但意义确是根本不一样的,
区别主要体现在下面几点:
- 首先getMeasureWidth()方法在measure()过程结束后就可以获取到了,而getWidth()方法要在layout()过程结束后才能获取到。
- getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,而getWidth()方法中的值则是通过layout(left,top,right,bottom)方法设置的。
动画:
插值器:interpolator,默认的插值器AccelerateDecelerateInterpolator
Android中共有3种动画,桢动画,补间动画,和属性动画。
- 桢动画: 类似幻灯片,一帧一帧的播放,使用<animation-list>标签,使用的类AnimationDrawable
- 补间动画:共分为4种 AlphaAnimation,ScaleAnimation,TranslateAnimation,RatateAnimation,可用在Activity条状时 overidePendingTransition(int enterAnim,int exitAnim)
- 属性动画:通过反射执行属性的get和set方法。可以设置监听器来监听动画完成的百分比,常用的类Animatorset,ObjectAnimator,ValueAnimator,插值器 估值器
Android 网络请求相关的api?
- HttpClilent:Android6.0 之后官方已经从资源包中剔除出来了,如果需要继续使用,可用用apach的jar包。
- HttpURLConnection ,官方推荐。
Android 中的多线程有哪些?
Thread ,Executors,Hanlder ,AsyncTask, HandlerThread,IntentService。
Hanlder的机制?
Android 的消息机制,也就是Handler的运行机制,Handler是靠Looper和MessageQueue来支撑的,Messagequeue就是消息队列,用来存储Message的,内部以单链表的方法插入和删除消息,Looper从名称上看是循环的意思,Messagequeue只是存储消息,但是不负责消息的处理,但是Looper就填补了这个功能,Looper以无限循环的方式不断从Messagequeue中轮询查看是否有新消息,如果有就处理,没有就一直等着。
Handler 不断将Msg压入到MsgQueue中,而Looper不断从MsgQueue中轮询Msg,然后 再将 dispatchMsg到对应的Handler中处理。
一个线程只有一个looper,(通过ThreadLocal控制),一个looper只能对应一个消息队列,(消息队列是在Looper创建时候同时创建的)一个消息队列可以有多个message,一个线程可以有多个hanlder。
Handler:主要用于发送消息。
Looper:主要用于轮询消息队列。
MsgQueue:主要存储消息 (链表结构 插入和删除有优势,数组在查询上有优势)。
Msg:要处理的任务请求。
Looper.looper ()方法为什么没有让主线程卡死?
- 首先一个线程如果执行完了,就结束了,在Android中的体现是如果主线程执行完了,那么就当前应用会退出,所以主线程从这方面来讲是必须无限循环的。
- 至于为什么没有卡死,主要平常主线程如果没有任务,当前线程就会进入到堵塞状态,释放CPU资源,消耗的资源很小,只有有任务才会唤醒当前线程。
ThreadLocal:并不是真的线程,只是以线程为作用域,存储当前线程中数据副本,looper就是靠ThreadLocal存储的。
ActivityThread:也是UI线程,默认会自动创建Looper
Android中为什么不允许子线程访问UI线程?
Android的UI线程是单线程的非安全的,如果多线程访问UI线程会使界面处于不可知的状态。如果加上锁会导致逻辑复杂,还有就是加上锁会导致UI线程执行效率低,因为加上锁会导致线程堵塞。
关于AsyncTask。
- onPreExecute(MainThread):开始任务前的准备工作,像加载进度条。
- doInBackground(WorkerThread):进行一些耗时任务,其中在publishProgress中更行进度。
- onProgressUpdate(MainThread):更新进度。
- onPostExecute(onPostExecute):得到doInBackground的执行结果。
其他:
AsyncTask在Android1.6之前是并行的,而在Android3.0之后是串行的.
线程池
corePoolSize:核心线程数。
maximumPoolSize:最大线程数。
keepAliveTime:当线程数量大于核心线程的时候,空余线程等待新任务执行的最大时间,否则会自动销毁。
unit:时间单位。
workQueue:线程池中的任务队列。
Java中的锁有哪些?
synchronized ReentrantLock.
- 公平锁/非公平锁,可重入锁,独享锁/共享锁,互斥锁/读写锁,乐观锁/悲观锁,分段锁。
wait:线程自动释放其占用的对象锁,并等待notify
notify:唤醒一个正在wait当前对象锁的线程,并让其拿到对象锁
notifyAll: 唤醒当前所有正在wait当前对象锁的线程 - CountDownLatch 控制多线程并发等待
进程和线程
线程:是CPU的调度的最小单元,同时线程也是一种有限的系统资源。
进程:指一个执行单元,在PC或者移动设备上指一个应用或者一个程序。
Android AIDL ,IPC
AIDL:Android Interface define Language
IPC: inner process community 进程间通讯
Aidl 是一种IPC的一种,Bindler也是IPC的一种,Aidl定义的接口实现了IBinder
Android中为什么需要AIDL?
- Android的架构是Client -Server ,也就是说如果需要使用server的音视频,或者其他一些服务,不能直接去使用,因为不在同一个进程中,这回设计到跨进程通信,跨进程通讯需要注意到不同进程使用的数据类型可能不同,而Aidl提供了一种方法,将请求的数据,或者返回的数据转换成俩个操作系统的基础数据,直接可以传输的数据。方便开发人员使用,开发人员不用去考虑底层的数据转换,这个可以看AIDL的定义。
- Android 进程之间不能共享数据的原因是 Android中每个应有都是一个进程,而每个应用都有一个单独的D a lvik实例,也就是说每个应用都有一个固定的大小,应用的占用的内存数据不能超过这个大小否则会内存溢出的问题。
- 如果多客户端,多进程之间使用服务的时候使用AIDL,
- 单个客户端访问服务,使用Binder就可以了,这个是google官方文档的推荐内容。
Android跨进程通讯的方式?
- Binder
- Bundle
- AiDL
- ContentProvider
- Intent
- 共享文件
- SharedPreferences(因为并发问题 ,会导致丢失数据)
- Socket
- Messenger
Binder
Binder是Android系统进程间(IPC)通讯方式之一,底层基于client-server的通讯方式,服务器端通过保存Binder实体对象,client端通过指针引用来调用。这里说的server是提供网络连接,音视频转码,解码的进程server,Client是指向这些Server发起请求的应用程序。Android中用的比较多的Binder是Service。Android Framwork层面来说Binder的主要用处是ServiceManger连接各种Manager(ActivityManger,WindowManger)和其他响应的ManagerService的桥梁。
aidl(其实非必需的,可以手写出来 )
服务端:通过创建一个Service来监听客户端的连接请求,创建AIDL文件,将暴露给客户端调用的接口在这个AIDL文件中声明,在Service中实现具体的AIDL接口。
客户端:绑定这个Service,绑定成功后,通过服务端返回的binder对象生成AIDL的类型,这样就可以调用AIDL中的方法了。
通过定义aidl接口,编译器自动生成跨进程通信的java接口。通过aidl传递的对象必须是序列化的实现parcelable或者Serializable
需要注意的是:
- 客户端调用服务端的向服务端请求的时候,当前客户端的线程会被挂起,直到服务端的进程返回才执行后面的操作,如果服务端有耗时操作,不要在UI线程进行请求,
- 服务端binder运行在binder线程池中,所以不管是否进行耗时都要进行同步操作。
AIDL支持的数据类型?
八种基本数据类型(double,long, int,float,short,boolean,byte,char)
List ,Map
String ,CharSequence
Parcelable以及AIDL
sqlite
public class SqliteDataBaseHelper extends SQLiteOpenHelper {
public static final String BOOK_TABLE = "book";
public static final String PERSON_TABLE = "person";
public static final String CREATEPERSONTABLE = "CREATE TABLE IF NOT EXIST" + PERSON_TABLE + "(_id INTEGEr PRIMARY KEY," + "name TEXT," + "sex TEXT)";
public static final String CREATEBOOKTABLE = "CREATE TABLE IF NOT EXIST " + BOOK_TABLE + "(_id INTEGER PRIMARY KEY, " + "name TEXT)";
public static final String databaseName = "contentprovider.db";
public SqliteDataBaseHelper(Context context) {
super(context, databaseName, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATEBOOKTABLE);
db.execSQL(CREATEPERSONTABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
insert into person (name,sex) values (valuse1,valuse2);
delete from person where name = value1
update person set sex ="女" where name = "value1"
select name from person
getX(),getRawX(),getY(),getRawY();
- getX()表示当前相对于当前view的位置。
*getRawX()表示相对于手机屏幕的位置。
scrollTo 与scrollBy的区别
scrollTo 是滑动的指定的距离。
scrollBy 是在原来的基础上滑动指定的距离。
scrollTo与scrollBy是滑动的view的内容,并不是互动view本身
Android中想让一个view移动,有哪些方法?
- scrollTo/scrollBy
- 动画
- 修改layoutparams布局参数
Android 事件分发机制?
- onDispatchTouchEvent:如果能够传递给当前view, 那么一定会调用,返回的结果受当前view的onTouchEvent和下级View的dispatchTouchEvent方法的影响。
- onIntercepetTouchEvent: 如果当前view拦截某个事件,那么在同一个事件序列中,此方法将不会再次调用,返回结果表示是否拦截当前事件。
- onTouchEcent:表示是否消耗当前事件,如果不消耗(在Action_Down中返回false),则在同一个事件序列中,当前View无法再次接受到事件。
Android 中的事件分发机制:当用户点击手机屏幕上的某个按钮时,在系统会从外到内,从Activity->ViewGroup->View,对事件进行分发,如果当前的某个层级处理了,将不会再向下传递,在Activity和View中没有onIntercpetTouchEvent事件,对于一个viewGroup而言,首先会调用DispatchTouchEvent,在其中进行onIntercpetTouchEvent处理,如果viewgroup自己处理(可以在move或者up中处理),那么会调用viewgroup的onTouchEvent,如果onIntercpetTouchEvent不处理,那么会交给子view的DispathTouchEvent处理,如此反复。
view处理事件时,如果它设置了OnToucheListener,那么onTouchListener中的onTouch就会回调,如果OnTouch
返回True,那么将不会调用onToucheEvent,如果返回false,将会调用onToucheEvent事件。
在View Group中的intercepte的Action_Down中处理当前事件,那么Action_Up或者Action_Move都会交给当前ViewGroup处理,这样会导致无法传递到子View中。
*onDisppatchTouchEvent:返回true表示消费当前事件,这个事件不会逆向传递了,直接结束了,如果返回false将不交给自己的TouchEvent处理了,如果有上级view,则交给上级view的TouchEvent处理,返回super表示向下传递,直到被消费掉。
- OnInterceptTouchEvent 如果返回true表示在当前ViewGroup中处理,会传递给自己的TouchEvent,然后传递给上级的TouchEvent,但是不会传递给子View事件。如果返回false/super表示可以继续交给下一个子View的dispatchTouchEvent处理。
- onToucheEvent:view返回true表示当前view处理了,不会再向上传递,view返回false或者super表示不会消费,将事件向上传递。
//不希望父布局拦截事件
getParent().requestDisallowInterceptTouchEvent(true);
Android 中finish()方法可能再onStop,onPause()方法之前调用。
Activity由于意外销毁到重新创建加载数据的方法:
onSaveInstanceState (Bundle bundle),onRestoreInstanceState(Bundle bundle)
Android横竖屏切换时,生命周期方法?
- 如果再Activity再清单文件中设置configChange =“orientation|screenSize”,不会重新创建Activity,只会从走onConfigurationChanged()方法。
- 如果只配置configChange = “orientation”Activity还是会重新创建1次,不会走onConfigurationChanged()方法。
- 如果不配置configChange,切横屏时执行1次,切竖屏时执行2次。
外接键盘配置android:configChanges="keyboardHidden|keyboard" ,会走onConfigurationChanged(),在生命周期走一次,如果不设置,会走生命周期方法2次。
Activity中onCreat()和onDestory()配对,标志Activity的创建和销毁,onStart()和onPause()表示Activity是否可见,onResume()和onStop()表示Activity是否在前台。
Android的启动模式?
理解前台栈和后台栈:前台栈是用户看见的,后台栈是用户看不到。
- stande : 是标准的启动模式,每次启动都会重新创建一个实例。
- singTop: 没如果当前的实例在栈顶不会重新创建,只会调用onNewIntent()方法。如果不在栈顶,则会重新创建。
- singleTask: 所在栈中实例唯一,如果前面创建了,则会清空在本实例创建之后的其他Activity,如果没有创建则会新建一个实例
- singleInstance: 表示在任务新建一个任务栈来创建实例。
- 如果前台栈是CD都是SingleTask,后台栈是AB,现在启动C,那么现在的任务列表是ABC,按返回直接回到B。
如果启动D,那么任务列表是ABCD ,按返回键将会退到C。 - A,B,C3 个Activity,B,C的的模式是SingleTask,并且通过TaskAffinity设置任务栈,现在启动A,A-B,B->C,C->A,A->B,现在点击返回回到A,再按返回键会回到桌面?
启动A在栈1 ,A->B,B在栈2,B->C,栈2是BC,C->A,栈1是A,栈2是BCA,再A->B,由于设置启动模式是singleTak,栈1是A,现在栈2的情况是B,按返回键的话是回到A,再按返回键将回到桌面。
onCreate中的Bundle 或onRestoreInstanceState中的Bundle有什么区别?
onCreate中的需要进行非空判断,而onRestoreInstanceState如果被调用,里面的Bundle是一定有值的。
Service 的onCreate,onStartCommand(),onDestory()方法都运行在UI线程
Service的启动方式有几种,以及生命周期方法有哪些?
- startService和bindService
- startService的生命周期方法有onCreate(),onStartCommand(),onDestory(),启动Service会调用onCreate(),onStartCommand()方法,如果多次启动只会调用一次onCreate()方法,onStartCommand()方法会被多次调用,onDestory()方法也只会调用一次。调用stopService()或者stopSelf()停止Service。
- bindService的生命周期方法有 onCreate(),onBinde(),多次运行这个俩个生命周期方法也只会走一次,由于绑定的Service与Activity有关系了,退出当前Activity必须要调用unbindService(),否则会引起ServiceConnect的泄漏。
- IntentService 内部有一个工作线程用来处理耗时操作,执行完成之后能够自动停止。
onStartCommand(Intent intent, int flags, int startId) 的返回值?
返回值的作用主要是当服务被kill之后,确认是否需要重新启动
- START_NOT_STICKY 不会重启服务
- START_REDELIVER_INTENT 会重启服务,并且intent对象不会被置空
- START_STICKY 会重启服务 但是intent会被置空
- START_STICKY_COMPATIBILITY 会重启服务,但是不会重启onStartCommand
ContentProvider对底层数据的存储没有要求,可以是Sqlite,也可以试文件,还可以是其他的任何类型。
Android中Window有哪些?
Activity,Toast,Dialog 等,
Android中Activity,View已经Window之间的关系?
开发者口中Activity可以作为一个页面,实际可以将Activity理解为一种特殊的View,一种可以让用户可以看的到视图,Window表示是对视图的一种抽象(本身Window也是抽象类),从它的定义看出它是一种顶级的视图外观和行为。如果想讲一个Actiivty在手机中显示出来,通过在ActivityThread.handleResumeActivity()调用r.activity.makeVisible(),将Activity和Window关联起来。通过Window->WindowMangerImpl->WindowMangeGlobal->ViewRootImpl 将Activity绘制出来。
Activity中子线程不能更新UI吗?
答案是能更新UI,不过是种特殊情况,在ViewRootImpl创建出来之前更新UI就行了。
gradle 打包流程?
大概分为以下几个步骤
1、使用aapt工具将res资源文件生成R.java文件
2、使用aidl工具将aidl文件生成对应java文件
3、使用javac命令编译工程源代码和上面两步生成的文件,生成class文件
4、通过dex工具将class文件和第三方jar包打成dex文件
5、用aapt工具将res下的资源文件编译成二进制文件,然后将其和上一步中的dex文件以及assets中的文件通过apkbuilder工具打包成apk文件
6、通过jarsigner对apk进行签名
7、利用zipalign工具对apk进行字节对齐优化操作
ScrollTo,与ScrollBy的区别?
scrollBy内部调用了scrollTo,它是基于当前位置的相对滑动;而scrollTo是绝对滑动,因此如果利用相同输入参数多次调用scrollTo()方法,由于View初始位置是不变只会出现一次View滚动的效果而不是多次。
引申:两者都只能对view内容进行滑动,而不能使view本身滑动,且非平滑,可使用Scroller有过渡滑动的效果
Android和Fragment的交互
Bunlder、接口回调,广播、Eventbus、
Actiivty的启动模式
standard(标准)总是会创建一个新的activity。
singleTop(栈顶)注意onNewIntent()方法的使用 ,可用通知栏,和搜索界面来举例。 如果启动的activity位与栈顶,则不会重复创建,回调onNewIntent()方法。如果不是位于栈顶,则会调用oncreate方法重复创建,和standard类似。
singleTask(栈复用) 整个栈内只会存在一个实例,
singleInstance(单例) 作为栈复用的加强版,每次创建activity都会创建一个新的任务栈,并保存该activity的实例,并保证没有其他activty进入。
BroadcastReceiver
和LocalBroadcastReceiver
有什么区别。
BroadcastReceiver
是针对应用间、应用和系统间、应用内部的一种通信方式。内部实现通过binder机制实现。
LocalBroadcastReceiver
是针对应用内部之间的的一种通信方式,应用外收不到,效率比较高。内部的实现是通过handler
网友评论