Androidスクリーンアダプティブ
原文の出典:
http://blog.csdn.net/lmj623565791/article/details/46695347
少し前、Androidスクリーンの適合案を送ったことを覚えています.このブログはWebページの設計で適合案を引き出し、最終的な目的はコントロールのサイズをパーセンテージで制御することです.もちろん、いくつかの問題があります.例えば、
もちろんandroid-percent-supportというライブラリは、基本的に上記の問題を解決することができますが、少し興奮しているのではないでしょうか.ちょっと待って、このsupport-libについて説明します.
このライブラリには次のものがあります.
PercentRelativeLayout
、PercentFrameLayout
の2つの容器類から受け継がれていることが名前でわかります.FrameLayout
、 RelativeLayout
、 layout_widthPercent
、 layout_heightPercent
、 layout_marginPercent
、 layout_marginLeftPercent
、 layout_marginTopPercent
、 layout_marginRightPercent
、 layout_marginBottomPercent
. サポート幅とmarginが表示されます.
つまり,
layout_marginStartPercent
,layout_marginEndPercent
を開発中にPercentRelativeLayout
,PercentFrameLayout
に置き換えるだけでよい.簡単ではありませんが、Linearlayoutはないようです.Linearlayoutにはweight属性があると言う人がいますね.しかし、weightプロパティは一方向しかサポートできません.ああ、大丈夫です.ちょうど
FrameLayout
をカスタマイズする機会を与えてくれました.では、本稿は3つの部分に分けられます.
RelativeLayout
、PercentLinearLayout
の使用PercentRelativeLayout
使用については、実は簡単で、githubにも例があります.android-percent-support-lib-sampleです.簡単に説明します.
まず覚えておいてください.gradle追加:
compile 'com.android.support:percent:22.2.0'
(一)PercentFrameLayout
3つのTextView、とても簡単で、直接効果図を見ます:
(二)PercentRelativeLayout
OK、依然として効果図を直接見ています.
使うことは何も言うことはありませんが、直感的に見てみましょう.
三、ソース分析
実際に考えてみると、Googleは私たちがよく知っていたRelativeLayoutとFrameLayoutの機能の拡張にすぎず、percent関連の属性をサポートしています.
では、この拡張子を追加したらどうするか考えてみましょう.
OK、上の予想があったら、
PercentFrameLayout
のソースコードを直接見ます.public class PercentFrameLayout extends FrameLayout {
private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
// ,
public PercentFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mHelper.handleMeasuredStateTooSmall()) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mHelper.restoreOriginalParams();
}
public static class LayoutParams extends FrameLayout.LayoutParams
implements PercentLayoutHelper.PercentLayoutParams {
private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
}
// ...
@Override
public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
return mPercentLayoutInfo;
}
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
}
}
}
コードはかなり短くて、
PercentLinearLayout
の中でまずgenerateLayoutParamsの方法を書き直したことを見ることができて、もちろん、いくつかの新しいlayout_をサポートしたためですプロパティは、対応するLayoutParamsを定義する必要があります.(一)percent関連属性の取得
PercentFrameLayoutが見えますLayoutParamsは元のFrameLayoutでLayoutParamsをベースに、PercentLayoutHelperを実現しました.PercentLayoutParamsインタフェース.
このインタフェースは簡単で、1つの方法しかありません.
public interface PercentLayoutParams {
PercentLayoutInfo getPercentLayoutInfo();
}
, , :return mPercentLayoutInfo;
, mPercentLayoutInfo ?
PercentFrameLayout.LayoutParams :
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
}
, attrs getPercentLayoutInfo , , , , PercentLayoutInfo , 。
:
public static PercentLayoutInfo getPercentLayoutInfo(Context context,
AttributeSet attrs) {
PercentLayoutInfo info = null;
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
-1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent width: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.widthPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent height: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.heightPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.leftMarginPercent = value;
info.topMarginPercent = value;
info.rightMarginPercent = value;
info.bottomMarginPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
-1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent left margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.leftMarginPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
-1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent top margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.topMarginPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
-1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent right margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.rightMarginPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
-1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent bottom margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.bottomMarginPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
-1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent start margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.startMarginPercent = value;
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
-1f);
if (value != -1f) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "percent end margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
info.endMarginPercent = value;
}
array.recycle();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "constructed: " + info);
}
return info;
}
たちの の と ているのではないでしょうか.すべての は にPercentLayoutInfoオブジェクトにカプセル されます.
OK、ここで たちの を します.これらの があれば、onMeasureで うのではないでしょうか.
( )onMeasueでchildの を する@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mHelper.handleMeasuredStateTooSmall()) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
onMeasure , mHelper , mHelper.adjustChildren 。
/**
* Iterates over children and changes their width and height to one calculated from percentage
* values.
* @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
* @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
*/
public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
//...
int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);
int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);
for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params instanceof PercentLayoutParams) {
PercentLayoutInfo info =
((PercentLayoutParams) params).getPercentLayoutInfo();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "using " + info);
}
if (info != null) {
if (params instanceof ViewGroup.MarginLayoutParams) {
info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params,
widthHint, heightHint);
} else {
info.fillLayoutParams(params, widthHint, heightHint);
}
}
}
}
}
この では、すべての を し、パーセントのプロパティで と さを することもできます.
まずwidthHint、heightHintで の 、 さを し、その すべての を し、LayoutParamsがPercentLayoutParamsタイプかどうかを し、もしそうであればparams.getPercentLayoutInfoはinfoオブジェクトを り します.
の でPercentLayoutInfoはpercent の を したことを えていますか.
infoがnullでない 、marginを する があるかどうかを する.fillLayoutParamsメソッドを てみましょう(marginの も ています). /**
* Fills {@code ViewGroup.LayoutParams} dimensions based on percentage values.
*/
public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
int heightHint) {
// Preserve the original layout params, so we can restore them after the measure step.
mPreservedParams.width = params.width;
mPreservedParams.height = params.height;
if (widthPercent >= 0) {
params.width = (int) (widthHint * widthPercent);
}
if (heightPercent >= 0) {
params.height = (int) (heightHint * heightPercent);
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
}
}
まず のwidthとheightを し、paramsのwidthとheightをリセットします.PercentFrameLayout
およびPercentFrameLayout
.
これで、 は たちのパーセンテージ は わり、 にはパーセンテージのサポートが しましたが、Googleはいくつかの を しています.
onMeasureメソッドに ります.@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mHelper.handleMeasuredStateTooSmall()) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
mHelper.handleMeasuredStateTooSmall , , , MeasuredSize , 。 :
public boolean handleMeasuredStateTooSmall() {
boolean needsSecondMeasure = false;
for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "should handle measured state too small " + view + " " + params);
}
if (params instanceof PercentLayoutParams) {
PercentLayoutInfo info =
((PercentLayoutParams) params).getPercentLayoutInfo();
if (info != null) {
if (shouldHandleMeasuredWidthTooSmall(view, info)) {
needsSecondMeasure = true;
params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
}
if (shouldHandleMeasuredHeightTooSmall(view, info)) {
needsSecondMeasure = true;
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
}
}
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
}
return needsSecondMeasure;
}
まずすべての を り、 のlayoutparamsを り し、PercentLayoutParamsのインスタンスであればinfoを り します.infoがnullでない は び します(int) (widthHint * widthPercent)
:private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 &&
info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
}
, measuredWidth measureHeight , layout_w/h WRAP_CONTENT , params.width / height= ViewGroup.LayoutParams.WRAP_CONTENT, 。
,onMeasure ~~~ , , ,but, onLayout , layout , onLayout :
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mHelper.restoreOriginalParams();
}
mHelper.restoreOriginalParams
/**
* Iterates over children and restores their original dimensions that were changed for
* percentage values. Calling this method only makes sense if you previously called
* {@link PercentLayoutHelper#adjustChildren(int, int)}.
*/
public void restoreOriginalParams() {
for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "should restore " + view + " " + params);
}
if (params instanceof PercentLayoutParams) {
PercentLayoutInfo info =
((PercentLayoutParams) params).getPercentLayoutInfo();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "using " + info);
}
if (info != null) {
if (params instanceof ViewGroup.MarginLayoutParams) {
info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
} else {
info.restoreLayoutParams(params);
}
}
}
}
}
ふふ、 は の を に し して、つまりonMeasureの の を えて、 が わった です.ここでは、レイアウトファイルの が0である に を します.リカバリは です.public void restoreLayoutParams(ViewGroup.LayoutParams params) {
params.width = mPreservedParams.width;
params.height = mPreservedParams.height;
}
~ , Ctrl+F ‘mPreservedParams.width’ 。
, , view v.getLayoutParams().width
, 0。
~ 0 , ~~
, , :
LayoutParamsにおける の onMeasureでparamsを する.widthはパーセンテージ であり、 が さすぎてw/hがwrap_である content, onLayoutでparamsをリセットする.w/hはレイアウトファイルに された である.
RelativeLayout、FrameLayoutの があって、 にもLinearLayoutのいくつかの がありません. いなことに、 たちのコアコードは(int) (heightHint * heightPercent);
でパッケージされており、 でLinearLayoutを しても ではありません.
、PercentLinearlayoutの
weightがあると う もいるかもしれませんが、weightは と さを にパーセントで えることができますか?
はい、コードは です. のようにします.
( )PercentLinearLayoutpackage com.juliengenoud.percentsamples;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.percent.PercentLayoutHelper;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;
/**
* Created by zhy on 15/6/30.
*/
public class PercentLinearLayout extends LinearLayout
{
private PercentLayoutHelper mPercentLayoutHelper;
public PercentLinearLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
mPercentLayoutHelper = new PercentLayoutHelper(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
mPercentLayoutHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mPercentLayoutHelper.handleMeasuredStateTooSmall())
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
mPercentLayoutHelper.restoreOriginalParams();
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends LinearLayout.LayoutParams
implements PercentLayoutHelper.PercentLayoutParams
{
private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
}
@Override
public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()
{
return mPercentLayoutInfo;
}
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)
{
PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
}
}
のソース を しく たら、このコードは されていないのではないでしょうか.
( )テストレイアウト
に したいくつかのTextViewは,それぞれ / さをパーセンテージとし, を5%pとした.
( )
OK、これで、ソース 、 PercentLinearLayoutを して わります.
PercentLinearLayoutの のアドレス:クリックして
ダウンロード:android-percent-support-extendにはandroid studio、eclipseプロジェクト、および のソースコードが まれています.