px dp sp 的区别
- px : 一个像素点 ,如手机分辨率320*480表示宽有320像素,高有480像素。
- dpi : dots per inch, 打印分辨率 (每英寸所能打印的点数,即打印精度)假如我们知道一部手机的分辨率是1080×1920,屏幕大小是5英寸,根据公式计算结果是440dpi。
dp=dip(Density-independent pixels),基于屏幕物理分辨率一个抽象的单位,用于说明与密度无关的尺寸和位置。
px = dp*dpi/160
dp = px*160/dpi
从公式可以看出,公式的由来是已160dpi的屏幕为基准,此时该屏幕1dp=1px。如果320dpi的屏幕上,公式推导可得1dp = 2px。
dp更类似一个物理尺寸,比如一张宽和高均为100dp的图片在320×480和480×800的手机上“看起来”一样大。而实际上,它们的像素值并不一样。dp正是这样一个尺寸,不管这个屏幕的密度是多少,屏幕上相同dp大小的元素看起来始终差不多大。 - sp :Scale-independent Pixels - 和dp单位很像,不过sp可以通过用户设置的字体大小而缩放. 为了能够自适应屏幕密度和用户的设置,建议设置字体大小时候用sp.当安卓系统字号设为“普通”时,sp=dp.
系统定义dpi图解
Inflater
将xml布局解析成视图view
在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而findViewById()是找xml布局文件下的具体widget控件(如Button、TextView等)。 具体作用: 1、对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来载入;
获得 LayoutInflater 实例的三种方式
1.LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater()
2.LayoutInflater localinflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
3. LayoutInflater inflater = LayoutInflater.from(context);
这三种方式最终本质是都是调用的Context.getSystemService()。
style/theme
可以将共同的属性拿出来放在styles.xml文件中
自定义控件
自定义的控件继承自View,初始化代码应该使用如下形式
public class CircleBackgroundView extends View {
public CircleBackgroundView(Context context) {
this(context, null);
}
public CircleBackgroundView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleBackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
}
之后在xml中跟正常控件一样使用就可以
对图像界面的刷新可以使用invalidate();
如下为画一个圆以及可以指定角度的圆弧的代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw background circle
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(5);
mPaint.setAntiAlias(true);
mCircleRectf.set(getWidth()/32, getWidth()/32, getWidth()*31/32, getWidth()*31/32);
canvas.drawArc(mCircleRectf,0,360,false,mPaint);
// draw the red Arc
mPaint.setColor(ContextCompat.getColor(getContext(),R.color.red));
mPaint.setAntiAlias(true);
canvas.drawArc(mCircleRectf, 270, mAngle, false, mPaint);
}
如下为暂停按钮的代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
// draw red background circle
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(ContextCompat.getColor(getContext(),R.color.red));
canvas.drawCircle(width/2,width/2,width/2,mPaint);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(5);
canvas.drawLine(width*4/10, width*3/10, width*4/10, width*7/10,mPaint);
canvas.drawLine(width*6/10, width*3/10, width*6/10, width*7/10,mPaint);
}
如下为开始按钮的代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
// draw red background circle
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(ContextCompat.getColor(getContext(),R.color.red));
canvas.drawCircle(width/2,width/2,width/2,mPaint);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(5);
mPath.moveTo(width*4/10,width*3/10);
mPath.lineTo(width*3/4,width/2);
mPath.lineTo(width*4/10,width*7/10);
mPath.close(); // 使这些点构成封闭的多边形
canvas.drawPath(mPath, mPaint);
}
如下为清空按钮的代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
// draw red background circle
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(5);
mCircleRectf.set(width/8, width/8, width*7/8, width*7/8);
canvas.drawArc(mCircleRectf, 270, 315, false, mPaint);
canvas.drawLine(width/8,width/8,width*7/20,width*7/20,mPaint);
}
其效果如下图
Fragment
Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:
使用Fragment最简单的一种方式,把Fragment当成普通的控件,直接写在Activity的布局文件中。步骤:
1、继承Fragment,重写onCreateView决定Fragemnt的布局
2、在Activity中声明此Fragment,就当和普通的View一样
如何管理Fragment?
查找Fragment
1. findFragmentById()
2. findFragmentByTag()
Fragment的后退
1. Fragment Stack
2. popBackStack()
3. addOnBackStackChangedListerner()
Fragment的管理
1. FragmentManager
2. FragmentTransaction
3. Add/Remove
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
在主线程中,使用handler很简单,new一个Handler对象实现其handleMessage方法,在handleMessage中
提供收到消息后相应的处理方法即可
新建message并通过handler发送消息代码
void startTimer() {
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
timeMessage = timeHandler.obtainMessage();
timeMessage.what = START_MESSAGE_CODE;
timeHandler.sendMessage(timeMessage);
}
};
timer.schedule(timerTask,0,10);
}
在handler类中重写一个handlerMessage的函数进行处理message
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity mainActivity = mainActivityWeakReference.get();
switch (msg.what) {
case START_MESSAGE_CODE:
totalCentiSceond++;
partCentiSecond++;
mainActivity.repaint(totalCentiSceond);
break;
case COUNT_MESSAGE_CODE:
mainActivity.repaint(totalCentiSceond);
mainActivity.addResult(partCentiSecond,totalCentiSceond);
partCentiSecond = 0;
break;
case CLEAR_MESSAGE_CODE:
totalCentiSceond = 0;
partCentiSecond = 0;
mainActivity.repaint(0);
mainActivity.clearResult();
break;
}
}