Sostituzione del meccanismo dei pesi linearLayout

Sfondo:

  • Google suggerisce di evitare di utilizzare i linearLayouts ponderati nidificati a causa della prestazione.
  • utilizzando il linearLayout ponderato nidificato è terribile leggere, scrivere e mantenere.
  • non c'è ancora alcuna buona alternativa per mettere le viste che sono% della dimensione disponibile. Solo le soluzioni sono pesi e utilizzando OpenGL. Non esiste nemless qualcosa come il "viewBox" mostrato in WPF / Silverlight per scalare le cose.

Questo è il motivo per cui ho deciso di creare il mio layout personale che racconta a ciascuno dei suoi figli esattamente quali dovrebbero essere i loro pesi (e i pesi circostanti) rispetto alle sue size.

  • Host di Android Usb riceve i dati
  • Android: Accesso al file da archiviazione interna utilizzando RandomAccessFile
  • Come aggiungere Spinner come elemento nel cassetto di navigazione
  • RxAndroidBle che mantiene una connessione persistente + scrittura / notifica gestione
  • Delphi XE5 Android: come rendere each articolo di listview ha il proprio model?
  • Come rilevare quando l'utente accende / spegne lo stato dei gps?
  • Sembra che sia riuscito, ma per qualche motivo penso che ci siano alcuni bug che non riesco a rintracciare.

    Uno dei bug è che TextView, anche se dò molto spazio per esso, mette il text in cima invece che al centro. L'image, d'altra parte, funziona molto bene. Un altro errore è che se utilizzo un layout (ad esempio un frameLayout) all'interno del layout personalizzato, le visualizzazioni all'interno di esso non verranno visualizzate (ma il layout stesso).

    Aiutaci a capire perché si verifica.

    Come usare: anziché l'uso successivo di layout lineare (uso un lungo XML per indicare come la mia soluzione può abbreviare le cose):

    <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> 

    Quello che faccio è semplicemente (x è where mettere la vista stessa nella list dei pesi):

     <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> 

    Il mio codice del layout speciale è:

     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; } } } 

  • Ottieni nome proprietario di un dispositivo Android
  • setLatestEventInfo non può essere risolto
  • Supporto api di Gestione Sip
  • SYSTEM_ALERT_WINDOW - Come get questa authorization automaticamente su Android 6.0 e targetSdkVersion 23
  • No ActionBar in PreferenceActivity dopo l'aggiornamento a Support Library v21
  • La versione ADT di Android è richiesta 20.0.0 e versioni successive
  • 4 Solutions collect form web for “Sostituzione del meccanismo dei pesi linearLayout”

    Ho accettato la tua sfida e ho cercato di creare il layout che descrivi in ​​risposta al mio commento. Hai ragione. È sorprendentemente difficile da realizzare. Oltre a questo, mi piace sparare le mosche delle case. Così ho salto a bordo e ho trovato questa soluzione.

    1. Estendere le classi di layout esistenti piuttosto che creare la propria da zero. Sono andato con RelativeLayout per iniziare ma lo stesso approccio può essere utilizzato da tutti. Ciò consente di utilizzare il comportmento predefinito per quel layout sulle viste per bambini che non si desidera manipolare.

    2. Ho aggiunto quattro attributi al layout chiamato in alto, a sinistra, in width e in altezza. La mia intenzione era quella di imitare HTML permettendo valori come "10%", "100px", "100dp" ecc. A questo punto l'unico valore accettato è un integer che rappresenta il% del genitore. "20" = 20% del layout.

    3. Per get performance migliori, consento al super.onLayout () di eseguire tutte le sue iterazioni e di manipolare solo le visualizzazioni con gli attributi personalizzati nell'ultimo passaggio. Poiché queste visioni saranno posizionate e scalate indipendentemente dai fratelli, possiamo spostarli dopo che tutto il resto si è stabilito.

    Ecco 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> 

    Ecco la mia class di layout.

     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; } } } 

    Ecco un esempio xml attività

     <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> 

    Ecco le immagini che ho usato per il test.

    bgimmettere qui la descrizione dell'immagine

    Se non si imposta un valore per un determinato attributo, verrà utilizzato il valore predefinito. Quindi se impostate la width ma non l'altezza l'image scala in width e wrap_content per l'altezza.

    Cartella del progetto Zipped.

    apk

    Ho trovato la fonte del bug. Il problema è che stavo usando il numero di figlio del layout come in indicatore di quante chiamate a onLayout farà. Questo non sembra ritenere vero nelle versioni precedenti di android. Ho notato in 2.1 onLayout viene chiamato solo una volta. Così ho cambiato

     if(pass == childCount){ 

    a

     if(true){ 

    e ha iniziato a lavorare come previsto.

    Penso ancora che sia utile modificare il layout solo dopo che il super è stato fatto. Basta trovare un modo migliore per sapere quando è.

    EDIT

    Non mi rendevo conto che la tua intenzione era quella di mettere insieme immagini con pixel per precisione di pixel. Ho raggiunto la precisione che stai cercando utilizzando le variables di precisione a doppio float anziché gli interi. Tuttavia, non sarai in grado di farlo, lasciando le tue immagini in scala. Quando vengono scalate le immagini, vengono aggiunti pixel ad un certo intervallo tra i pixel esistenti. Il colore dei nuovi pixel è una media ponderata dei pixel circostanti. Quando le immagini vengono scalate indipendentemente l'una dall'altra non condividono informazioni. Il risultato è che avnetworking sempre qualche artefatto alla cucitura. Aggiungere a questo il risultato dell'arrotondamento poiché non è ansible avere un pixel parziale e avrai sempre una tolleranza di pixel di +/- 1.

    Per verificare questo, è ansible tentare la stessa attività nel software di editing delle foto premium. Io uso PhotoShop. Utilizzando le stesse immagini come nella mia apk, li ho inseriti in file separati. Li ho scalati sia dal 168% verticalmente che dal 127% in orizzontale. Li ho quindi inseriti in un file insieme e ho cercato di allinearli. Il risultato è stato esattamente lo stesso che è visto nella mia apk.

    Per dimostrare l'esattezza del layout, ho aggiunto una seconda attività al mio apk. Su questa attività non ho scalato l'image di background. Tutto il resto è esattamente lo stesso. Il risultato è senza soluzione di continuità.

    Ho anche aggiunto un button per mostrare / hide l'image sovrapposta e uno per passare tra le attività.

    Ho aggiornato sia l'apk che la cartella del progetto zipped sul mio drive google. Puoi farli dai link sopra riportti.

    Dopo aver provato il tuo codice, trovo solo la ragione dei problemi che hai accennato e è perché nel tuo layout personalizzato è sufficiente disporre correttamente il bambino, tuttavia hai dimenticato di misurare correttamente il tuo figlio, che influenzerà direttamente la gerarchia del disegno. basta aggiungere il codice riportto di seguito e funziona per me.

      @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)); } 

    Ora c'è una soluzione più bella del layout personalizzato che ho fatto:

    PercentRelativeLayout

    Qui puoi trovare un tutorial e troverai qui un repo.

    Codice di esempio:

     <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/> 

    o:

      <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/> 

    Mi chiedo se sia in grado di gestire le questioni che ho mostrato qui.

    Propongo di utilizzare le seguenti ottimizzazioni:

     <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> 

    o utilizzare http://developer.android.com/reference/android/widget/LinearLayout.html#attr_android:weightSum

    o utilizzare TableLayout con layout_weight per righe e colonne

    o utilizzare GridLayout.

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.