android⾯试题谈谈屏幕适配
⾯试问你屏幕适配,那么你要知道为什么Android要做屏幕适配,因为Android是开源的, 各⼤⼚商不仅可以对软件定制,还可以对硬件定制,这样就造成市场上不同分辨率的⼿机超多,现在估计得有⼏万或者⼏⼗万种,这就导致android设备的碎⽚化很严重。所以还是做ios很⾟福啊,下⾯对⼀些概念弄清楚
屏幕尺⼨:指屏幕的对⾓线的长度,单位是英⼨,1英⼨=2.54厘⽶
屏幕分辨率:是指横纵上的像素点 单位是px 1px = 1个像素点 ⼀般是以纵向像素*横向像素 ⽐如1920*1080 ⼀个屏幕分辨率越⾼ 显⽰效果就越好
屏幕像素密度:是指每英⼨上的像素点数 单位是dpi 是dot per inch 的缩写,屏幕像素密度与屏幕尺⼨以及屏幕分辨率有关
以Google的Nexus5为例,它的分辨率是1920*1080 它的屏幕尺⼨是4.95inch 屏幕像素密度是445 这是怎么计算出来的呢?
1920*1920+1080*1080这值是4852800 然后开根号再除以4.95就得到是445.03175153177745
像素:构成图像的最⼩单位 美⼯或者设计师使⽤
dip:density independent pixels 是指密度 与像素⽆关以160dpi为基准,1dip = 1px 和dp⼀样
加⼊有⼆个设备 ⼀个480*320 密度是160dpi. 另外⼀台是800*480像素密度是240dpi
⽐如你要在这⼆个屏幕上要TextView的宽度充满横屏除了使⽤match_parent还可以使⽤如下:
我们知道480*320 它的宽度是320px,它是以160dpi为基准的,1px = 1dip 那么它的宽度就是320px就可以 但是在800*480也就是说它的宽度是480px,该如何计算呢?这个也很简单,240/160=1/x; 求这x是多少1.5 相当于1dp = 1.5px 那么它的宽度就是320*1.5其实这就是我们做屏幕适配使⽤到的核⼼技术,想要适配所有⼿机都是这么适配的。
我们在创建Android项⽬的时候 系统会帮助我们⽣成
drawable_mdpi
drawable_hdpi
drawable_xdpi
drawable_xxdpi
drawable_xxxdpi
对应的密度如下:
上⾯是讲了基本的概念, 下⾯谈谈如何去适配?
第⼀种⽅案:限定符适配
分辨率限定符 drawable-hdpi drawable-xdpi drawable-xxdpi
尺⼨限定符layout-small layout-large
最⼩宽度限定符:values-sw360dp values-sw384dp
屏幕⽅向限定符:layout_port layout-land
这种⽅案⼏乎不⽤,除⾮在⼀些很⼩公司 做出来的app没啥⼈⽤, ⼤点的额公司肯定不⽤这套⽅案,⽐如我⼀张图⽚要放在不同的分辨率下 不但给美⼯同事添加了⼯作量,app打包后体积⼀定会增⼤,维护起来很⿇烦。
第⼆种⽅案:⾃定义像素适配
这种适配⽬前是最好的,⼏乎能适配市⾯上所有的适配 当初在上⾯公司 交给test in ⼀个三⽅的测试公司, 测试了600多设备 都没出现问题,所以这种很靠谱
实现⽅案:以美⼯的设计尺⼨为原始尺⼨,根据不同设备的密度 计算出宽和⾼
代码如下:
public class UIAdapter {
private static volatile UIAdapter instance = null;
/
/设计师的参考尺⼨
private static final float defaultWidth = 1080;
private static final float defaultHeight = 1920;
//屏幕的真实尺⼨
private int screenWidth;
private int screenHeight;
private UIAdapter(){
}
public static UIAdapter getInstance(){
if(null==instance){
synchronized (UIAdapter.class){
if(null==instance){
instance = new UIAdapter();
}
}
}
return instance;
}
public void init(Context context) {
if(null==context){
return;
}
WindowManager wm = (WindowManager) ApplicationContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
if(displayMetrics.widthPixels>displayMetrics.heightPixels){//横屏
screenWidth = displayMetrics.heightPixels;
screenHeight = displayMetrics.widthPixels;
}else{
screenWidth = displayMetrics.widthPixels;
screenHeight = displayMetrics.heightPixels-getStatusBarHeight(context);
}
}
/**
* 获取状态栏⾼度
* @param context
* @return
*/
public static int getStatusBarHeight(Context context) {
Resources resources = Resources();
int resourceId = Identifier("status_bar_height", "dimen", "android");
int height = DimensionPixelSize(resourceId);
return height;
}
public float scaleX(){
return screenWidth/defaultWidth;
}
public float scaleY(){
return screenHeight/defaultHeight;
}
public void scaleView(View v, int w, int h, int l, int t, int r, int b) {
if(v==null){
return;
}
w = (int) (w*scaleX());
h = (int) (h*scaleY());
l = (int) (l*scaleX());
t = (int) (t*scaleY());
r = (int) (r*scaleX());
b = (int) (b*scaleY());
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); if (params != null) {
params.width = w;
params.height = h;
params.setMargins(l, t, r, b);
}
}
}
记得在Application初始化下:
public class MyApp extends Application {
@Override
public void onCreate() {
}
}
使⽤:
如果想显⽰屏幕的1/3的话就是360了宽度,是根据设计师给出来的宽度进⾏设置
第三种⽅案: 百分⽐适配
这是Google 提出来的⼀个解决适配⽅案,想要使⽤必须添加依赖:
implementation 'com.android.support:percent:28.0.0'
主要就⼆个类:
PercentRelativeLayout
PercentFrameLayout
主要属性如下:
app:layout_heightPercent:⽤百分⽐表⽰⾼度
app:layout_widthPercent:⽤百分⽐表⽰宽度
app:layout_marginPercent:⽤百分⽐表⽰View之间的间隔
app:layout_marginLeftPercent:⽤百分⽐表⽰左边间隔
app:layout_marginRight:⽤百分⽐表⽰右边间隔
app:layout_marginTopPercent:⽤百分⽐表⽰顶部间隔
app:layout_marginBottomPercent:⽤百分⽐表⽰底部间隔
app:layout_marginStartPercent:⽤百分⽐表⽰距离第⼀个View之间的距离
app:layout_marginEndPercent:⽤百分⽐表⽰距离最后⼀个View之间的距离
app:layout_aspectRatio:⽤百分⽐表⽰View的宽⾼⽐
简单的布局看看:
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout
xmlns:android="schemas.android/apk/res/android"
xmlns:app="schemas.android/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/textview"
android:background="#f0f000"
app:layout_heightPercent="50%"
app:layout_widthPercent="50%"
android:text="Hello World!"
android:gravity="center"
/
>
</android.support.percent.PercentRelativeLayout>
其实真实的项⽬中都没⽤过,那么它的实现原理是什么样的,因为现在⾯试不问你怎么使⽤,怎么使⽤它时初级⼯程师⼲的活,做了3到5年的⼈怎么去跟哪些刚毕业或者从事2年的⽐,那么这个时候⽐的就是内功了,怎么体现你⽐那些⼈⽜逼呢?看PercentRelativeLayout的源码⼤概知道它怎么弄的,我们根据它的源代码返照写个,我们在分析view的加载流程中你的xml布局怎么⽣成对应的类⽂件,在这就不分析view的加载流程了,我们在Activity中写的setContentView()是调⽤了PhoneWindow中setContentView():
@Override
public void setContentView(int layoutResID) {
// 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)) {
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = SceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
}
mContentParentExplicitlySet = true;
}
layoutResId就是我们的xml布局,看这段代码:
mLayoutInflater.inflate(layoutResID, mContentParent);
最终会调⽤LayoutInflater中的
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
⽅法⾥⾯有⼀段很关键的代码:
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = ateLayoutParams(attrs);
if (!attachToRoot) {
/
margin属性值可以为百分比/ Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
root变量可以看作是你布局中的根view
params = ateLayoutParams(attrs);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论