Intereting Posts
Android: как разделить код между проектами, подписанными с тем же сертификатом Как определить, когда фрагмент становится видимым в ViewPager Recycliewiew мучительно медленно загружает кешированные изображения из Picasso Как обновить значение столбца базы данных базы данных android sqlite до значения null, используя ContentValues? Как проверить, открыт или нет диалог? Почему я НЕ получаю исключение из памяти? Как закрепить файл APK в Android? Android ListView, который не прокручивается? Когда действительно используется пакет savedInstanceState? Как установить название DialogFragment? Доступ запрещен при создании хранилища ключей для приложения для Android Получить абсолютный путь при выборе изображения из галереи kitkat android Android: токен Firebase при первом запуске равен нулю Mockito + Dexmaker на Android Декодировать аппаратное обеспечение декодированного устройства H264 с помощью ffmpeg в режиме реального времени

Замена для механизма линейных весов

Задний план:

  • Google предлагает избегать использования вложенных взвешенных линейных листов из-за производительности.
  • Использование вложенных взвешенных linearLayout ужасно читать, писать и поддерживать.
  • По-прежнему нет хорошей альтернативы для размещения просмотров, которые составляют% от имеющегося размера. Только решения – это вес и использование OpenGL. В WPF / Silverlight нет ничего похожего на «viewBox» для автоматического масштабирования.

Вот почему я решил создать свой собственный макет, который вы рассказываете каждому из своих детей точно, какими должны быть их веса (и окружающие веса) по сравнению с его размером.

Кажется, я преуспел, но почему-то я думаю, что есть некоторые ошибки, которые я не могу отследить.

Одна из ошибок заключается в том, что textView, хотя я даю им много места, он помещает текст сверху, а не в центр. ImageViews, с другой стороны, работают очень хорошо. Другая ошибка заключается в том, что если я использую макет (например, frameLayout) внутри моего настраиваемого макета, представления внутри него не будут отображаться (но сам макет).

Пожалуйста, помогите мне выяснить, почему это происходит.

Как использовать: вместо следующего использования линейного макета (я использую длинный XML по назначению, чтобы показать, как мое решение может сократить количество вещей):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <View android:layout_width="wrap_content" android:layout_height="0px" android:layout_weight="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1" android:orientation="horizontal"> <View android:layout_width="0px" android:layout_height="wrap_content" android:layout_weight="1" /> <TextView android:layout_width="0px" android:layout_weight="1" android:layout_height="match_parent" android:text="@string/hello_world" android:background="#ffff0000" android:gravity="center" android:textSize="20dp" android:textColor="#ff000000" /> <View android:layout_width="0px" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout> <View android:layout_width="wrap_content" android:layout_height="0px" android:layout_weight="1" /> </LinearLayout> 

То, что я делаю, просто (х – это место, где нужно поместить представление в список весов):

 <com.example.weightedlayouttest.WeightedLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.example.weightedlayouttest" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:layout_width="0px" android:layout_height="0px" app:horizontalWeights="1,1x,1" app:verticalWeights="1,1x,1" android:text="@string/hello_world" android:background="#ffff0000" android:gravity="center" android:textSize="20dp" android:textColor="#ff000000" /> </com.example.weightedlayouttest.WeightedLayout> 

Мой код специального макета:

 public class WeightedLayout extends ViewGroup { @Override protected WeightedLayout.LayoutParams generateDefaultLayoutParams() { return new WeightedLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT); } @Override public WeightedLayout.LayoutParams generateLayoutParams(final AttributeSet attrs) { return new WeightedLayout.LayoutParams(getContext(),attrs); } @Override protected ViewGroup.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p) { return new WeightedLayout.LayoutParams(p.width,p.height); } @Override protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p) { final boolean isCorrectInstance=p instanceof WeightedLayout.LayoutParams; return isCorrectInstance; } public WeightedLayout(final Context context) { super(context); } public WeightedLayout(final Context context,final AttributeSet attrs) { super(context,attrs); } public WeightedLayout(final Context context,final AttributeSet attrs,final int defStyle) { super(context,attrs,defStyle); } @Override protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b) { for(int i=0;i<this.getChildCount();++i) { final View v=getChildAt(i); final WeightedLayout.LayoutParams layoutParams=(WeightedLayout.LayoutParams)v.getLayoutParams(); // final int availableWidth=rl; final int totalHorizontalWeights=layoutParams.getLeftHorizontalWeight()+layoutParams.getViewHorizontalWeight()+layoutParams.getRightHorizontalWeight(); final int left=l+layoutParams.getLeftHorizontalWeight()*availableWidth/totalHorizontalWeights; final int right=r-layoutParams.getRightHorizontalWeight()*availableWidth/totalHorizontalWeights; // final int availableHeight=bt; final int totalVerticalWeights=layoutParams.getTopVerticalWeight()+layoutParams.getViewVerticalWeight()+layoutParams.getBottomVerticalWeight(); final int top=t+layoutParams.getTopVerticalWeight()*availableHeight/totalVerticalWeights; final int bottom=b-layoutParams.getBottomVerticalWeight()*availableHeight/totalVerticalWeights; // v.layout(left+getPaddingLeft(),top+getPaddingTop(),right+getPaddingRight(),bottom+getPaddingBottom()); } } // /////////////// // LayoutParams // // /////////////// public static class LayoutParams extends ViewGroup.LayoutParams { int _leftHorizontalWeight =0,_rightHorizontalWeight=0,_viewHorizontalWeight=0; int _topVerticalWeight =0,_bottomVerticalWeight=0,_viewVerticalWeight=0; public LayoutParams(final Context context,final AttributeSet attrs) { super(context,attrs); final TypedArray arr=context.obtainStyledAttributes(attrs,R.styleable.WeightedLayout_LayoutParams); { final String horizontalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_horizontalWeights); // // handle horizontal weight: // final String[] words=horizontalWeights.split(","); boolean foundViewHorizontalWeight=false; int weight; for(final String word : words) { final int viewWeightIndex=word.lastIndexOf('x'); if(viewWeightIndex>=0) { if(foundViewHorizontalWeight) throw new IllegalArgumentException("found more than one weights for the current view"); weight=Integer.parseInt(word.substring(0,viewWeightIndex)); setViewHorizontalWeight(weight); foundViewHorizontalWeight=true; } else { weight=Integer.parseInt(word); if(weight<0) throw new IllegalArgumentException("found negative weight:"+weight); if(foundViewHorizontalWeight) _rightHorizontalWeight+=weight; else _leftHorizontalWeight+=weight; } } if(!foundViewHorizontalWeight) throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value"); } // // handle vertical weight: // { final String verticalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_verticalWeights); final String[] words=verticalWeights.split(","); boolean foundViewVerticalWeight=false; int weight; for(final String word : words) { final int viewWeightIndex=word.lastIndexOf('x'); if(viewWeightIndex>=0) { if(foundViewVerticalWeight) throw new IllegalArgumentException("found more than one weights for the current view"); weight=Integer.parseInt(word.substring(0,viewWeightIndex)); setViewVerticalWeight(weight); foundViewVerticalWeight=true; } else { weight=Integer.parseInt(word); if(weight<0) throw new IllegalArgumentException("found negative weight:"+weight); if(foundViewVerticalWeight) _bottomVerticalWeight+=weight; else _topVerticalWeight+=weight; } } if(!foundViewVerticalWeight) throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value"); } // arr.recycle(); } public LayoutParams(final int width,final int height) { super(width,height); } public LayoutParams(final ViewGroup.LayoutParams source) { super(source); } public int getLeftHorizontalWeight() { return _leftHorizontalWeight; } public void setLeftHorizontalWeight(final int leftHorizontalWeight) { _leftHorizontalWeight=leftHorizontalWeight; } public int getRightHorizontalWeight() { return _rightHorizontalWeight; } public void setRightHorizontalWeight(final int rightHorizontalWeight) { if(rightHorizontalWeight<0) throw new IllegalArgumentException("negative weight :"+rightHorizontalWeight); _rightHorizontalWeight=rightHorizontalWeight; } public int getViewHorizontalWeight() { return _viewHorizontalWeight; } public void setViewHorizontalWeight(final int viewHorizontalWeight) { if(viewHorizontalWeight<0) throw new IllegalArgumentException("negative weight:"+viewHorizontalWeight); _viewHorizontalWeight=viewHorizontalWeight; } public int getTopVerticalWeight() { return _topVerticalWeight; } public void setTopVerticalWeight(final int topVerticalWeight) { if(topVerticalWeight<0) throw new IllegalArgumentException("negative weight :"+topVerticalWeight); _topVerticalWeight=topVerticalWeight; } public int getBottomVerticalWeight() { return _bottomVerticalWeight; } public void setBottomVerticalWeight(final int bottomVerticalWeight) { if(bottomVerticalWeight<0) throw new IllegalArgumentException("negative weight :"+bottomVerticalWeight); _bottomVerticalWeight=bottomVerticalWeight; } public int getViewVerticalWeight() { return _viewVerticalWeight; } public void setViewVerticalWeight(final int viewVerticalWeight) { if(viewVerticalWeight<0) throw new IllegalArgumentException("negative weight :"+viewVerticalWeight); _viewVerticalWeight=viewVerticalWeight; } } } часть public class WeightedLayout extends ViewGroup { @Override protected WeightedLayout.LayoutParams generateDefaultLayoutParams() { return new WeightedLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT); } @Override public WeightedLayout.LayoutParams generateLayoutParams(final AttributeSet attrs) { return new WeightedLayout.LayoutParams(getContext(),attrs); } @Override protected ViewGroup.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p) { return new WeightedLayout.LayoutParams(p.width,p.height); } @Override protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p) { final boolean isCorrectInstance=p instanceof WeightedLayout.LayoutParams; return isCorrectInstance; } public WeightedLayout(final Context context) { super(context); } public WeightedLayout(final Context context,final AttributeSet attrs) { super(context,attrs); } public WeightedLayout(final Context context,final AttributeSet attrs,final int defStyle) { super(context,attrs,defStyle); } @Override protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b) { for(int i=0;i<this.getChildCount();++i) { final View v=getChildAt(i); final WeightedLayout.LayoutParams layoutParams=(WeightedLayout.LayoutParams)v.getLayoutParams(); // final int availableWidth=rl; final int totalHorizontalWeights=layoutParams.getLeftHorizontalWeight()+layoutParams.getViewHorizontalWeight()+layoutParams.getRightHorizontalWeight(); final int left=l+layoutParams.getLeftHorizontalWeight()*availableWidth/totalHorizontalWeights; final int right=r-layoutParams.getRightHorizontalWeight()*availableWidth/totalHorizontalWeights; // final int availableHeight=bt; final int totalVerticalWeights=layoutParams.getTopVerticalWeight()+layoutParams.getViewVerticalWeight()+layoutParams.getBottomVerticalWeight(); final int top=t+layoutParams.getTopVerticalWeight()*availableHeight/totalVerticalWeights; final int bottom=b-layoutParams.getBottomVerticalWeight()*availableHeight/totalVerticalWeights; // v.layout(left+getPaddingLeft(),top+getPaddingTop(),right+getPaddingRight(),bottom+getPaddingBottom()); } } // /////////////// // LayoutParams // // /////////////// public static class LayoutParams extends ViewGroup.LayoutParams { int _leftHorizontalWeight =0,_rightHorizontalWeight=0,_viewHorizontalWeight=0; int _topVerticalWeight =0,_bottomVerticalWeight=0,_viewVerticalWeight=0; public LayoutParams(final Context context,final AttributeSet attrs) { super(context,attrs); final TypedArray arr=context.obtainStyledAttributes(attrs,R.styleable.WeightedLayout_LayoutParams); { final String horizontalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_horizontalWeights); // // handle horizontal weight: // final String[] words=horizontalWeights.split(","); boolean foundViewHorizontalWeight=false; int weight; for(final String word : words) { final int viewWeightIndex=word.lastIndexOf('x'); if(viewWeightIndex>=0) { if(foundViewHorizontalWeight) throw new IllegalArgumentException("found more than one weights for the current view"); weight=Integer.parseInt(word.substring(0,viewWeightIndex)); setViewHorizontalWeight(weight); foundViewHorizontalWeight=true; } else { weight=Integer.parseInt(word); if(weight<0) throw new IllegalArgumentException("found negative weight:"+weight); if(foundViewHorizontalWeight) _rightHorizontalWeight+=weight; else _leftHorizontalWeight+=weight; } } if(!foundViewHorizontalWeight) throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value"); } // // handle vertical weight: // { final String verticalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_verticalWeights); final String[] words=verticalWeights.split(","); boolean foundViewVerticalWeight=false; int weight; for(final String word : words) { final int viewWeightIndex=word.lastIndexOf('x'); if(viewWeightIndex>=0) { if(foundViewVerticalWeight) throw new IllegalArgumentException("found more than one weights for the current view"); weight=Integer.parseInt(word.substring(0,viewWeightIndex)); setViewVerticalWeight(weight); foundViewVerticalWeight=true; } else { weight=Integer.parseInt(word); if(weight<0) throw new IllegalArgumentException("found negative weight:"+weight); if(foundViewVerticalWeight) _bottomVerticalWeight+=weight; else _topVerticalWeight+=weight; } } if(!foundViewVerticalWeight) throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value"); } // arr.recycle(); } public LayoutParams(final int width,final int height) { super(width,height); } public LayoutParams(final ViewGroup.LayoutParams source) { super(source); } public int getLeftHorizontalWeight() { return _leftHorizontalWeight; } public void setLeftHorizontalWeight(final int leftHorizontalWeight) { _leftHorizontalWeight=leftHorizontalWeight; } public int getRightHorizontalWeight() { return _rightHorizontalWeight; } public void setRightHorizontalWeight(final int rightHorizontalWeight) { if(rightHorizontalWeight<0) throw new IllegalArgumentException("negative weight :"+rightHorizontalWeight); _rightHorizontalWeight=rightHorizontalWeight; } public int getViewHorizontalWeight() { return _viewHorizontalWeight; } public void setViewHorizontalWeight(final int viewHorizontalWeight) { if(viewHorizontalWeight<0) throw new IllegalArgumentException("negative weight:"+viewHorizontalWeight); _viewHorizontalWeight=viewHorizontalWeight; } public int getTopVerticalWeight() { return _topVerticalWeight; } public void setTopVerticalWeight(final int topVerticalWeight) { if(topVerticalWeight<0) throw new IllegalArgumentException("negative weight :"+topVerticalWeight); _topVerticalWeight=topVerticalWeight; } public int getBottomVerticalWeight() { return _bottomVerticalWeight; } public void setBottomVerticalWeight(final int bottomVerticalWeight) { if(bottomVerticalWeight<0) throw new IllegalArgumentException("negative weight :"+bottomVerticalWeight); _bottomVerticalWeight=bottomVerticalWeight; } public int getViewVerticalWeight() { return _viewVerticalWeight; } public void setViewVerticalWeight(final int viewVerticalWeight) { if(viewVerticalWeight<0) throw new IllegalArgumentException("negative weight :"+viewVerticalWeight); _viewVerticalWeight=viewVerticalWeight; } } } 

Solutions Collecting From Web of "Замена для механизма линейных весов"

Я принял ваш вызов и попытался создать макет, который вы описываете, в ответ на мои комментарии. Ты прав. Это удивительно сложно. Кроме того, мне нравится стрелять в домашних мух. Поэтому я прыгнул на борт и придумал это решение.

  1. Расширяйте существующие классы макетов, а не создавайте свои собственные с нуля. Я пошел с RelativeLayout для начала, но тот же подход может использоваться всеми из них. Это дает вам возможность использовать поведение по умолчанию для этого макета для дочерних представлений, которые вы не хотите манипулировать.

  2. Я добавил четыре атрибута в макет, названный top, left, width и height. Мое намерение состояло в том, чтобы имитировать HTML, позволяя такие значения, как «10%», «100 пикселей», «100 дп» и т. Д. В это время единственным принятым значением является целое число, представляющее% родительского. «20» = 20% от макета.

  3. Для лучшей производительности я разрешаю super.onLayout () выполнять все его итерации и манипулировать представлениями с помощью настраиваемых атрибутов на последнем проходе. Поскольку эти взгляды будут позиционироваться и масштабироваться независимо от братьев и сестер, мы можем перемещать их после того, как все остальное уладится.

Здесь atts.xml

 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="HtmlStyleLayout"> <attr name="top" format="integer"/> <attr name="left" format="integer"/> <attr name="height" format="integer"/> <attr name="width" format="integer"/> </declare-styleable> </resources> 

Вот мой класс макета.

 package com.example.helpso; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RelativeLayout; public class HtmlStyleLayout extends RelativeLayout{ private int pass =0; @Override protected HtmlStyleLayout.LayoutParams generateDefaultLayoutParams() { return new HtmlStyleLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT); } @Override public HtmlStyleLayout.LayoutParams generateLayoutParams(final AttributeSet attrs) { return new HtmlStyleLayout.LayoutParams(getContext(),attrs); } @Override protected RelativeLayout.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p) { return new HtmlStyleLayout.LayoutParams(p.width,p.height); } @Override protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p) { final boolean isCorrectInstance=p instanceof HtmlStyleLayout.LayoutParams; return isCorrectInstance; } public HtmlStyleLayout(Context context, AttributeSet attrs) { super(context, attrs); } public void setScaleType(View v){ try{ ((ImageView) v).setScaleType (ImageView.ScaleType.FIT_XY); }catch (Exception e){ // The view is not an ImageView } } @Override protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b) { super.onLayout(changed, l, t, r, b); //Let the parent layout do it's thing pass++; // After the last pass of final int childCount = this.getChildCount(); // the parent layout if(true){ // we do our thing for(int i=0;i<childCount;++i) { final View v=getChildAt(i); final HtmlStyleLayout.LayoutParams params = (HtmlStyleLayout.LayoutParams)v.getLayoutParams(); int newTop = v.getTop(); // set the default value int newLeft = v.getLeft(); // of these to the value int newBottom = v.getBottom(); // set by super.onLayout() int newRight= v.getRight(); boolean viewChanged = false; if(params.getTop() >= 0){ newTop = ( (int) ((bt) * (params.getTop() * .01)) ); viewChanged = true; } if(params.getLeft() >= 0){ newLeft = ( (int) ((rl) * (params.getLeft() * .01)) ); viewChanged = true; } if(params.getHeight() > 0){ newBottom = ( (int) ((int) newTop + ((bt) * (params.getHeight() * .01))) ); setScaleType(v); // set the scale type to fitxy viewChanged = true; }else{ newBottom = (newTop + (v.getBottom() - v.getTop())); Log.i("heightElse","v.getBottom()=" + Integer.toString(v.getBottom()) + " v.getTop=" + Integer.toString(v.getTop())); } if(params.getWidth() > 0){ newRight = ( (int) ((int) newLeft + ((rl) * (params.getWidth() * .01))) ); setScaleType(v); viewChanged = true; }else{ newRight = (newLeft + (v.getRight() - v.getLeft())); } // only call layout() if we changed something if(viewChanged) Log.i("SizeLocation", Integer.toString(i) + ": " + Integer.toString(newLeft) + ", " + Integer.toString(newTop) + ", " + Integer.toString(newRight) + ", " + Integer.toString(newBottom)); v.layout(newLeft, newTop, newRight, newBottom); } pass = 0; // reset the parent pass counter } } public class LayoutParams extends RelativeLayout.LayoutParams { private int top, left, width, height; public LayoutParams(final Context context, final AttributeSet atts) { super(context, atts); TypedArray a = context.obtainStyledAttributes(atts, R.styleable.HtmlStyleLayout); top = a.getInt(R.styleable.HtmlStyleLayout_top , -1); left = a.getInt(R.styleable.HtmlStyleLayout_left, -1); width = a.getInt(R.styleable.HtmlStyleLayout_width, -1); height = a.getInt(R.styleable.HtmlStyleLayout_height, -1); a.recycle(); } public LayoutParams(int w, int h) { super(w,h); Log.d("lp","2"); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); Log.d("lp","3"); } public LayoutParams(ViewGroup.MarginLayoutParams source) { super(source); Log.d("lp","4"); } public int getTop(){ return top; } public int getLeft(){ return left; } public int getWidth(){ return width; } public int getHeight(){ return height; } } } 

Вот пример активности xml

 <com.example.helpso.HtmlStyleLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:html="http://schemas.android.com/apk/res/com.example.helpso" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/imageView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:scaleType="fitXY" android:src="@drawable/bg" /> <ImageView android:id="@+id/imageView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/overlay" html:height="10" html:left="13" html:top="18" html:width="23" /> </com.example.helpso.HtmlStyleLayout> 

Вот изображения, которые я использовал для тестирования.

Б.Г.Введите описание изображения здесь

Если вы не установите значение для определенного атрибута, оно будет использоваться по умолчанию. Поэтому, если вы задаете ширину, но не высоту, изображение будет масштабироваться по ширине и wrap_content для высоты.

Почтовая папка проекта.

APK

Я нашел источник ошибки. Проблема в том, что я использовал подсчет дочернего элемента макета, как в индикаторе того, сколько звонков в onLayout он сделает. В старых версиях Android это не работает. Я заметил в 2.1 onLayout только один раз. Поэтому я изменил

 if(pass == childCount){ 

в

 if(true){ 

И он начал работать, как ожидалось.

Я все еще думаю, что полезно настроить макет только после того, как супер сделано. Просто нужно найти лучший способ узнать, когда это.

РЕДАКТИРОВАТЬ

Я не понимал, что вы намерены собирать изображения с точностью до пикселя. Я достиг точности, которую вы ищете, используя переменные точности double float вместо целых чисел. Тем не менее, вы не сможете выполнить это, позволяя своим изображениям масштабироваться. Когда изображения масштабируются, пиксели добавляются с некоторым интервалом между существующими пикселями. Цвет новых пикселей представляет собой средневзвешенное значение окружающих пикселей. Когда вы масштабируете изображения независимо друг от друга, они не передают никакой информации. В результате вы всегда будете иметь артефакт в шве. Добавьте к этому результат округления, поскольку у вас не может быть частичный пиксель, и вы всегда будете иметь допуск +/- 1 пиксель.

Чтобы убедиться в этом, вы можете попробовать одну и ту же задачу в своем программном обеспечении для редактирования фотографий премиум-класса. Я использую PhotoShop. Используя те же изображения, что и в моем apk, я поместил их в отдельные файлы. Я увеличил их на 168% по вертикали и 127% по горизонтали. Затем я поместил их в файл и попытался выровнять их. Результат был таким же, как и в моем апке.

Чтобы продемонстрировать точность макета, я добавил вторую активность в свой apk. В этом упражнении я не масштабировал фоновое изображение. Все остальное точно такое же. Результат получается бесшовным.

Я также добавил кнопку, чтобы показать / скрыть изображение наложения, а другое – переключиться между действиями.

Я обновил как apk, так и папку с zipped-проектом на моем диске Google. Вы можете получить их по ссылкам выше.

Попробовав свой код, я просто нахожу причину проблем, о которых вы упомянули, и это связано с тем, что в вашем настраиваемом макете вы правильно размещаете ребенка, однако вы забыли правильно измерить своего ребенка, что напрямую повлияет на иерархию чертежа, поэтому Просто добавьте код ниже, и он работает для меня.

  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec)-this.getPaddingRight()-this.getPaddingRight(); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec)-this.getPaddingTop()-this.getPaddingBottom(); int heightMode = MeasureSpec.getMode(heightMeasureSpec); if(heightMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.UNSPECIFIED) throw new IllegalArgumentException("the layout must have a exact size"); for (int i = 0; i < this.getChildCount(); ++i) { View child = this.getChildAt(i); LayoutParams lp = (LayoutParams)child.getLayoutParams(); int width = lp._viewHorizontalWeight * widthSize/(lp._leftHorizontalWeight+lp._rightHorizontalWeight+lp._viewHorizontalWeight); int height = lp._viewVerticalWeight * heightSize/(lp._topVerticalWeight+lp._bottomVerticalWeight+lp._viewVerticalWeight); child.measure(width | MeasureSpec.EXACTLY, height | MeasureSpec.EXACTLY); } this.setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); } 

Теперь есть более приятное решение, чем пользовательский макет, который я сделал:

PercentRelativeLayout

Здесь можно найти учебное пособие, и здесь можно найти репо.

Пример кода:

 <android.support.percent.PercentRelativeLayout 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"/> <ImageView app:layout_widthPercent="50%" app:layout_heightPercent="50%" app:layout_marginTopPercent="25%" app:layout_marginLeftPercent="25%"/> </android.support.percent.PercentFrameLayout/> 

или:

  <android.support.percent.PercentFrameLayout 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"/> <ImageView app:layout_widthPercent="50%" app:layout_heightPercent="50%" app:layout_marginTopPercent="25%" app:layout_marginLeftPercent="25%"/> </android.support.percent.PercentFrameLayout/> 

Интересно, хотя, если он может справиться с проблемами, которые я показал здесь.

Я предлагаю использовать следующие оптимизации:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" android:background="#ffff0000" android:gravity="center" android:textSize="20dp" android:textColor="#ff000000" /> </FrameLayout> 

Или используйте http://developer.android.com/reference/android/widget/LinearLayout.html#attr_android:weightSum

Или используйте TableLayout с layout_weight для строк и столбцов

Или используйте GridLayout.