2017-07-11 73 views
10

Stworzyłem niestandardowy obraz w kształcie. Działa dobrze, jeśli używasz go w trybie przewijania. Ale kiedy próbowałem go użyć w recyklingu, zaobserwowałem dziwne zachowanie. Obrazy nie są rysowane i pokazują odstęp (patrz pierwszy obraz), chyba że przewiniesz w dół (patrz drugi obraz). To samo dzieje się, gdy przewiniesz w górę.RecyclerView z elementami o niestandardowym kształcie

Chcę wiedzieć, jak uniknąć tych luk. Czy możesz wskazać mi, gdzie robię źle? Dzięki za pomoc.

Stan początkowy lub po przewinięciu się:

Initial state

po przewinięciu w dół:

After scrolling down

import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Paint.Style; 
import android.graphics.Path; 
import android.support.v7.widget.AppCompatImageView; 
import android.util.AttributeSet; 

/** 
* Created by santalu on 7/4/17. 
*/ 

public class DiagonalImageView extends AppCompatImageView { 

    public static final int TOP = 0; 
    public static final int MIDDLE = 1; 
    public static final int BOTTOM = 2; 

    private final Path mClipPath = new Path(); 
    private final Path mLinePath = new Path(); 

    private final Paint mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 

    private int mPosition; 
    private int mOverlap; 
    private int mLineColor; 
    private int mLineSize; 

    private boolean mMaskEnabled = true; 

    public DiagonalImageView(Context context) { 
     super(context); 
     init(context, null); 
    } 

    public DiagonalImageView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(context, attrs); 
    } 

    private void init(Context context, AttributeSet attrs) { 
     if (attrs == null) { 
      return; 
     } 

     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShowCaseImageView); 
     try { 
      mPosition = a.getInt(R.styleable.DiagonalImageView_di_position, TOP); 
      mOverlap = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_overlap, 0); 
      mLineSize = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_lineSize, 0); 
      mLineColor = a.getColor(R.styleable.DiagonalImageView_di_lineColor, Color.BLACK); 

      mLinePaint.setColor(mLineColor); 
      mLinePaint.setStyle(Style.STROKE); 
      mLinePaint.setStrokeWidth(mLineSize); 
     } finally { 
      a.recycle(); 
     } 
    } 

    public void setPosition(int position, boolean maskEnabled) { 
     mMaskEnabled = maskEnabled; 
     setPosition(position); 
    } 

    public void setPosition(int position) { 
     if (mPosition != position) { 
      mClipPath.reset(); 
      mLinePath.reset(); 
     } 
     mPosition = position; 
    } 

    @Override protected void onDraw(Canvas canvas) { 
     int saveCount = canvas.getSaveCount(); 
     canvas.clipPath(mClipPath); 
     super.onDraw(canvas); 
     canvas.drawPath(mLinePath, mLinePaint); 
     canvas.restoreToCount(saveCount); 
    } 

    @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     super.onLayout(changed, left, top, right, bottom); 
     if (!changed) { 
      return; 
     } 

     if (mMaskEnabled && mClipPath.isEmpty()) { 
      int width = getMeasuredWidth(); 
      int height = getMeasuredHeight(); 

      if (width <= 0 || height <= 0) { 
       return; 
      } 

      switch (mPosition) { 
       case TOP: 
        mClipPath.moveTo(0, 0); 
        mClipPath.lineTo(width, 0); 
        mClipPath.lineTo(width, height - mOverlap); 
        mClipPath.lineTo(0, height); 

        mLinePath.moveTo(0, height); 
        mLinePath.lineTo(width, height - mOverlap); 
        break; 
       case MIDDLE: 
        mClipPath.moveTo(0, mOverlap); 
        mClipPath.lineTo(width, 0); 
        mClipPath.lineTo(width, height - mOverlap); 
        mClipPath.lineTo(0, height); 

        mLinePath.moveTo(0, height); 
        mLinePath.lineTo(width, height - mOverlap); 
        break; 
       case BOTTOM: 
        mClipPath.moveTo(0, mOverlap); 
        mClipPath.lineTo(width, 0); 
        mClipPath.lineTo(width, height); 
        mClipPath.lineTo(0, height); 
        break; 
      } 
      mClipPath.close(); 
      mLinePath.close(); 
     } 
    } 
} 

I obejmuje aplikację przykładową tutaj wykazać problem, jeśli jesteś zainteresowany

import android.content.Context; 
import android.graphics.Rect; 
import android.os.Bundle; 
import android.support.v7.app.AppCompatActivity; 
import android.support.v7.widget.LinearLayoutManager; 
import android.support.v7.widget.RecyclerView; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 

import com.santalu.showcaseimageview.ShowCaseImageView; 

public class MainActivity extends AppCompatActivity { 

    @Override protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     int overlap = getResources().getDimensionPixelSize(R.dimen.overlap_size); 
     RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); 
     LinearLayoutManager layoutManager = new LinearLayoutManager(this); 
     recyclerView.setLayoutManager(layoutManager); 
     recyclerView.setHasFixedSize(true); 
     recyclerView.addItemDecoration(new OverlapItemDecoration(-overlap)); 
     recyclerView.setAdapter(new SampleAdapter(this)); 
    } 

    static class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.ViewHolder> { 
     private final Context mContext; 

     SampleAdapter(Context context) { 
      mContext = context; 
     } 

     @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
      return new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item, parent, false)); 
     } 

     @Override public void onBindViewHolder(ViewHolder holder, int position) { 
      holder.bind(position); 
     } 

     @Override public int getItemCount() { 
      return 7; 
     } 

     class ViewHolder extends RecyclerView.ViewHolder { 
      DiagonalImageView image; 
      //int overlap; 

      ViewHolder(View itemView) { 
       super(itemView); 
       image = (DiagonalImageView) itemView.findViewById(R.id.image); 
       //overlap = -mContext.getResources().getDimensionPixelSize(R.dimen.overlap_size); 
      } 

      void bind(int position) { 
       boolean maskEnabled = getItemCount() > 1; 
       //MarginLayoutParams params = (MarginLayoutParams) image.getLayoutParams(); 
       if (position == 0) { 
        image.setPosition(ShowCaseImageView.TOP, maskEnabled); 
        //params.setMargins(0, 0, 0, 0); 
       } else if (position == getItemCount() - 1) { 
        image.setPosition(ShowCaseImageView.BOTTOM, maskEnabled); 
        //params.setMargins(0, overlap, 0, 0); 
       } else { 
        image.setPosition(ShowCaseImageView.MIDDLE, maskEnabled); 
        //params.setMargins(0, overlap, 0, 0); 
       } 
       //image.setLayoutParams(params); 
      } 
     } 
    } 

    static class OverlapItemDecoration extends RecyclerView.ItemDecoration { 
     private int mOverlap; 

     OverlapItemDecoration(int overlap) { 
      mOverlap = overlap; 
     } 

     @Override 
     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 
      super.getItemOffsets(outRect, view, parent, state); 
      if (parent.getChildAdapterPosition(view) != 0) { 
       outRect.top = mOverlap; 
      } 
     } 
    } 
} 

item.xml

<?xml version="1.0" encoding="utf-8"?> 
<com.santalu.diagonalimageview.DiagonalImageView 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/image" 
    android:layout_width="wrap_content" 
    android:layout_height="@dimen/image_height" 
    android:scaleType="centerCrop" 
    android:src="@drawable/demo" 
    app:csi_lineColor="@color/deep_orange" 
    app:csi_lineSize="@dimen/line_size" 
    app:csi_overlap="@dimen/overlap_size"/> 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> 
<android.support.v7.widget.RecyclerView 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/recycler_view" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"/> 

Odpowiedz

8

Po pewnym resea rch i spróbujcie zorientować się, że wartości nie są poprawne i dobrze zaprojektowane specjalnie dla granicy. Niektóre przypadki zachodzą na siebie i pomyślałem, że powoduje to, że obrazy nie są rysowane prawidłowo.

Przeprojektowałem widok i wprowadziłem poprawki. Dla przyszłych czytelników oto kod końcowy:

/** 
* Created by santalu on 7/4/17. 
* 
* Note: if position set NONE mask won't be applied 
* 
* POSITION DIRECTION 
* 
* TOP   LEFT | RIGHT 
* BOTTOM  LEFT | RIGHT 
* LEFT  TOP | BOTTOM 
* RIGHT  TOP | BOTTOM 
*/ 

public class DiagonalImageView extends AppCompatImageView { 

    private static final String TAG = DiagonalImageView.class.getSimpleName(); 

    public static final int NONE = 0; 
    public static final int TOP = 1; 
    public static final int RIGHT = 2; 
    public static final int BOTTOM = 4; 
    public static final int LEFT = 8; 

    private final Path mClipPath = new Path(); 
    private final Path mBorderPath = new Path(); 

    private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 

    private int mPosition; 
    private int mDirection; 
    private int mOverlap; 
    private int mBorderColor; 
    private int mBorderSize; 

    private boolean mBorderEnabled; 

    public DiagonalImageView(Context context) { 
     super(context); 
     init(context, null); 
    } 

    public DiagonalImageView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(context, attrs); 
    } 

    private void init(Context context, AttributeSet attrs) { 
     if (attrs == null) { 
      return; 
     } 

     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiagonalImageView); 
     try { 
      mPosition = a.getInteger(R.styleable.DiagonalImageView_di_position, NONE); 
      mDirection = a.getInteger(R.styleable.DiagonalImageView_di_direction, RIGHT); 
      mOverlap = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_overlap, 0); 
      mBorderSize = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_borderSize, 0); 
      mBorderColor = a.getColor(R.styleable.DiagonalImageView_di_borderColor, Color.BLACK); 
      mBorderEnabled = a.getBoolean(R.styleable.DiagonalImageView_di_borderEnabled, false); 

      mBorderPaint.setColor(mBorderColor); 
      mBorderPaint.setStyle(Style.STROKE); 
      mBorderPaint.setStrokeWidth(mBorderSize); 
     } finally { 
      a.recycle(); 
     } 
    } 

    public void set(int position, int direction) { 
     if (mPosition != position || mDirection != direction) { 
      mClipPath.reset(); 
      mBorderPath.reset(); 
     } 
     mPosition = position; 
     mDirection = direction; 
     postInvalidate(); 
    } 

    public void setPosition(int position) { 
     if (mPosition != position) { 
      mClipPath.reset(); 
      mBorderPath.reset(); 
     } 
     mPosition = position; 
     postInvalidate(); 
    } 

    public void setDirection(int direction) { 
     if (mDirection != direction) { 
      mClipPath.reset(); 
      mBorderPath.reset(); 
     } 
     mDirection = direction; 
     postInvalidate(); 
    } 

    public void setBorderEnabled(boolean enabled) { 
     mBorderEnabled = enabled; 
     postInvalidate(); 
    } 

    @Override protected void onDraw(Canvas canvas) { 
     if (mClipPath.isEmpty()) { 
      super.onDraw(canvas); 
      return; 
     } 

     int saveCount = canvas.save(); 
     canvas.clipPath(mClipPath); 
     super.onDraw(canvas); 
     if (!mBorderPath.isEmpty()) { 
      canvas.drawPath(mBorderPath, mBorderPaint); 
     } 
     canvas.restoreToCount(saveCount); 
    } 

    @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     super.onLayout(changed, left, top, right, bottom); 
     if (!changed) { 
      return; 
     } 

     if (mClipPath.isEmpty()) { 
      int width = getMeasuredWidth(); 
      int height = getMeasuredHeight(); 

      if (width <= 0 || height <= 0) { 
       return; 
      } 

      mClipPath.reset(); 
      mBorderPath.reset(); 

      switch (mPosition) { 
       case TOP: 
        if (mDirection == LEFT) { 
         mClipPath.moveTo(0, 0); 
         mClipPath.lineTo(width, mOverlap); 
         mClipPath.lineTo(width, height); 
         mClipPath.lineTo(0, height); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(0, 0); 
          mBorderPath.lineTo(width, mOverlap); 
         } 
        } else { 
         mClipPath.moveTo(0, mOverlap); 
         mClipPath.lineTo(width, 0); 
         mClipPath.lineTo(width, height); 
         mClipPath.lineTo(0, height); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(0, mOverlap); 
          mBorderPath.lineTo(width, 0); 
         } 
        } 
        break; 
       case RIGHT: 
        if (mDirection == TOP) { 
         mClipPath.moveTo(0, 0); 
         mClipPath.lineTo(width, 0); 
         mClipPath.lineTo(width - mOverlap, height); 
         mClipPath.lineTo(0, height); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(width, 0); 
          mBorderPath.lineTo(width - mOverlap, height); 
         } 
        } else { 
         mClipPath.moveTo(0, 0); 
         mClipPath.lineTo(width - mOverlap, 0); 
         mClipPath.lineTo(width, height); 
         mClipPath.lineTo(0, height); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(width - mOverlap, 0); 
          mBorderPath.lineTo(width, height); 
         } 
        } 
        break; 
       case BOTTOM: 
        if (mDirection == LEFT) { 
         mClipPath.moveTo(0, 0); 
         mClipPath.lineTo(width, 0); 
         mClipPath.lineTo(width, height - mOverlap); 
         mClipPath.lineTo(0, height); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(0, height); 
          mBorderPath.lineTo(width, height - mOverlap); 
         } 
        } else { 
         mClipPath.moveTo(0, 0); 
         mClipPath.lineTo(width, 0); 
         mClipPath.lineTo(width, height); 
         mClipPath.lineTo(0, height - mOverlap); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(0, height - mOverlap); 
          mBorderPath.lineTo(width, height); 
         } 
        } 
        break; 
       case LEFT: 
        if (mDirection == TOP) { 
         mClipPath.moveTo(0, 0); 
         mClipPath.lineTo(width, 0); 
         mClipPath.lineTo(width, height); 
         mClipPath.lineTo(mOverlap, height); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(0, 0); 
          mBorderPath.lineTo(mOverlap, height); 
         } 
        } else { 
         mClipPath.moveTo(mOverlap, 0); 
         mClipPath.lineTo(width, 0); 
         mClipPath.lineTo(width, height); 
         mClipPath.lineTo(0, height); 

         if (mBorderEnabled) { 
          mBorderPath.moveTo(mOverlap, 0); 
          mBorderPath.lineTo(0, height); 
         } 
        } 
        break; 
      } 

      mClipPath.close(); 
      mBorderPath.close(); 
     } 
    } 
} 

Aktualizacja: wydałem to na Github jako biblioteka w przypadku, gdy jest to potrzebne Diagonal ImageView

1

trzeba zastosować układ cięcia dla rzeczy tutaj jest wiersz XML Link https://github.com/florent37/DiagonalLayout

<com.github.florent37.diagonallayout.DiagonalLayout 
    android:layout_width="match_parent" 
    android:layout_height="250dp" 
    android:elevation="10dp" 
    app:diagonal_angle="20" 
    app:diagonal_position="top" 
    app:diagonal_direction="right"> 

    <ImageView 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:scaleType="centerCrop" 
     android:src="@drawable/mountains" /> 

+1

Dzięki za komentarz. Tak, znam tę bibliotekę. Ale jak widać na zrzucie ekranu, pod warunkiem, że elementy recyclerview zachodzą na siebie, a wszystkie elementy mają swój własny separator (pomarańczowa linia). Ścieżka klipu obrazu i dzielnika obliczona na podstawie wysokości nakładki, a nie kąt, aby upewnić się, że każdy element jest zgodny z ich ograniczeniami i poprawnie narysowany. Chcę tylko wiedzieć, co jest nie tak z moim widokiem lub implementacją recyclerview. Ale dzięki za sugestię – santalu

+0

Oprócz tego samego problemu występuje również w tej bibliotece. Spróbuj użyć go w widoku recyclerview. – santalu

+0

czy ustawiłeś pozycję przekątną, w XML powinien być taki, jak ta przekątna: diagonal_position = "bottom". Nie jestem pewien, ale jeśli sprawdzasz ostatni indeks, możesz użyć innego układu, w którym pozycja przekątnej jest ustawiona na dole. – farhana