根據UI設計標註的屏幕尺寸做爲參考,在View的加載過程,根據當前設備的實際像素換算成目標像素,再做用到控件上。php
public int getAdapterWidth(int width) {
return width * mDisplayWidth / DEFAULT_WIDTH;
}
public int getAdapterHeight(int height) {
return height * mDisplayHeight / DEFAULT_HEIGHT;
}
複製代碼
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!isAdapted) {
int childCnt = getChildCount();
for (int i = 0; i < childCnt; i++) {
LayoutParams params = (LayoutParams) getChildAt(i).getLayoutParams();
params.width = ScreenUtils.getInstance(getContext()).getAdapterWidth(params.width);
params.height = ScreenUtils.getInstance(getContext()).getAdapterWidth(params.height);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
複製代碼
原理和上面的自定義View的像素佈局相似,經過自定義屬性來計算View的尺寸。java
Google已經支持百分比佈局android
implementation 'com.android.support:percent:29.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
複製代碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent">
<androidx.percentlayout.widget.PercentRelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_widthPercent="50%" app:layout_heightPercent="50%" android:background="@android:color/holo_red_light"/>
</androidx.percentlayout.widget.PercentRelativeLayout>
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1">
<TextView android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" android:text="Hello World!" app:layout_constraintWidth_percent=".5" app:layout_constraintHeight_percent=".5" android:background="@android:color/holo_red_light"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
複製代碼
注意:ConstraintLayout支持百分比佈局,從1.1.X開始bash
/** * Container for a dynamically typed data value. Primarily used with * {@link android.content.res.Resources} for holding resource values. */
public class TypedValue {
/** * Converts an unpacked complex data value holding a dimension to its final floating * point value. The two parameters <var>unit</var> and <var>value</var> * are as in {@link #TYPE_DIMENSION}. * * @param unit The unit to convert from. * @param value The value to apply the unit to. * @param metrics Current display metrics to use in the conversion -- * supplies display density and scaling information. * * @return The complex floating point value multiplied by the appropriate * metrics depending on its unit. */
public static float applyDimension(int unit, float value, DisplayMetrics metrics) {
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
}
複製代碼
示例代碼app
public class DensityUtil {
/** * 設計師標註的標準寬度,單位是DP */
private static final int STANDARD_WIDTH = 640;
private static float mDensity;
private static float mScaleDensity;
private static float mScreenWidth;
private DensityUtil() {
}
public static void setDensity(Activity activity) {
if (mDensity == 0) {
final Application application = activity.getApplication();
DisplayMetrics appDm = application.getResources().getDisplayMetrics();
mDensity = appDm.density;
mScaleDensity = appDm.scaledDensity;
// appDm.widthPixels始終都是短的邊,不管是否設置了橫豎屏
// 與經過WindowManager獲取的不同
mScreenWidth = appDm.widthPixels;
/** * 監聽設置中字體大小的更改 */
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(@NonNull Configuration configuration) {
if (configuration != null && configuration.fontScale > 0) {
// 從新獲取字體的縮放
mScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
// 會從新執行Activity的onCreate方法,即從新執行後面的設置代碼
}
}
@Override
public void onLowMemory() {
}
});
}
// 經過標準屏幕寬dp和實際屏幕寬度px,計算將實際屏幕px換算成標準dp,對應的density值
float targetDensity = mScreenWidth / STANDARD_WIDTH;
// 經過新的density計算新的scaleDensity
float targetScaleDensity = mScaleDensity * (targetDensity / mDensity);
float densityDpi = targetDensity * 160;
DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();
activityDm.density = targetDensity;
activityDm.scaledDensity = targetScaleDensity;
activityDm.densityDpi = (int) densityDpi;
}
}
複製代碼
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) {
DensityUtil.setDensity(activity);
}
......
});
}
}
複製代碼