2016-08-12 14 views
2

Viewクラスのみを拡張するカスタムビューを作成しました。カスタムビューは、RecyclerView内で使用される場合を除いて、完全に機能します。これは、カスタムビューです:RecyclerViewと互換性のあるAndroidカスタムビューを作成する方法

public class KdaBar extends View { 
    private int mKillCount, mDeathCount, mAssistCount; 
    private int mKillColor, mDeathColor, mAssistColor; 
    private int mViewWidth, mViewHeight; 
    private Paint mKillBarPaint, mDeathBarPaint, mAssistBarPaint, mBgPaint; 
    private float mKillPart, mDeathPart, mAssistPart; 

    public KdaBar(Context context, AttributeSet attrs) { 
     super(context, attrs); 

     TypedArray a = context.getTheme().obtainStyledAttributes(
       attrs, 
       R.styleable.KdaBar, 
       0, 0); 

     try { 
      mKillCount = a.getInt(R.styleable.KdaBar_killCount, 0); 
      mDeathCount = a.getInt(R.styleable.KdaBar_deathCount, 0); 
      mAssistCount = a.getInt(R.styleable.KdaBar_assistCount, 0); 

      mKillColor = a.getColor(R.styleable.KdaBar_killBarColor, ContextCompat.getColor(getContext(), R.color.kill_score_color)); 
      mDeathColor = a.getColor(R.styleable.KdaBar_deathBarColor, ContextCompat.getColor(getContext(), R.color.death_score_color)); 
      mAssistColor = a.getColor(R.styleable.KdaBar_assistBarColor, ContextCompat.getColor(getContext(), R.color.assist_score_color)); 
     } finally { 
      a.recycle(); 
     } 

     init(); 
    } 

    public void setValues(int killCount, int deathCount, int assistCount) { 

     mKillCount = killCount; 
     mDeathCount = deathCount; 
     mAssistCount = assistCount; 

     invalidate(); 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 

     canvas.drawRect(0f, 0f, mViewWidth, mViewHeight, mBgPaint); 
     canvas.drawRect(mKillPart+mDeathPart, 0f, mKillPart+mDeathPart+mAssistPart, mViewHeight, mAssistBarPaint); 
     canvas.drawRect(mKillPart, 0f, mKillPart+mDeathPart, mViewHeight, mDeathBarPaint); 
     canvas.drawRect(0f, 0f, mKillPart, mViewHeight, mKillBarPaint); 
    } 

    @Override 
    protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld){ 
     super.onSizeChanged(xNew, yNew, xOld, yOld); 

     mViewWidth = xNew; 
     mViewHeight = yNew; 

     float total = mKillCount + mDeathCount + mAssistCount; 
     mKillPart = (mKillCount/total) * mViewWidth; 
     mDeathPart = (mDeathCount/total) * mViewWidth; 
     mAssistPart = (mAssistCount/total) * mViewWidth; 
    } 

    private void init() { 
     mKillBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mKillBarPaint.setColor(mKillColor); 

     mDeathBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mDeathBarPaint.setColor(mDeathColor); 

     mAssistBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mAssistBarPaint.setColor(mAssistColor); 

     mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mBgPaint.setColor(ContextCompat.getColor(getContext(), R.color.transparent)); 
    } 
} 

リンクされた画像は、カスタムビューは、現在(カスタムビューは中央に数字上の長方形で)どのようなものか、そのバーの下http://imgur.com/a/Ib5Yl

数字が自分を表していますあなたが気づいていない場合は色分けされています。最初の項目の値が0の場合、カスタムビューに青色のバーが表示されないことは明らかです。奇妙なことに、私は知っている。

値は(それがRecyclerView.Adapter <内部にある>)がセットされる以下の方法がある:

@Override 
public void onBindViewHolder(ViewHolder holder, int position) { 
    MatchHistory.Match item = mDataset.get(position); 
    MatchHistory.MatchPlayer[] players = item.getPlayers(); 

    for(MatchHistory.MatchPlayer player: players) { 
     int steamId32 = (int) Long.parseLong(mCurrentPlayer.getSteamId()); 
     if (steamId32 == player.getAccountId()) { 
      mCurrentMatchPlayer = player; 
     } 
    } 
    ... 
    holder.mKdaBar.setValues(mCurrentMatchPlayer.getKills(), mCurrentMatchPlayer.getDeaths(), mCurrentMatchPlayer.getAssists()); 
    ... 
} 

これはonCreateViewHolderある:

@Override 
public MatchesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_match_item, parent, false); 
    ViewHolder vh = new ViewHolder(v); 
    return vh; 
} 

とViewHolderクラス:

public static class ViewHolder extends RecyclerView.ViewHolder { 
    KdaBar mKdaBar; 

    public ViewHolder(View v) { 
     super(v); 
     ... 
     mKdaBar = (KdaBar) v.findViewById(R.id.kda_bar); 
     ... 
    } 
} 

私は、データセットがアダプタによって使用されると、項目の位置が時々変更されます(同時にフェッチされますが、データセットが順序付けされるように挿入されるため)。私は、データセット内のアイテムの位置を変更しないこともテストしましたが、まだ良い結果は得られていないことを忘れていました。イメージをチェックすると、アイテム内に他の情報があることがわかります。カスタムビューのデータを除いて、それらがすべて正しいことを100%確信しています。

私はオーバーライドする必要があるいくつかの方法を忘れていると思っていますが、すでに多くのチュートリアルがあり、この問題については触れていません。この問題を解決することを楽しみにしています。 TIA!

+0

もっとコードを共有できますか?カスタムビューは単にキャンバスの描画ですか? mCurrentMatchPlayerはどのように設定しますか? onCreateViewHolderは何をするのですか – napkinsterror

+0

@napkinsterrorはい、カスタムビューはキャンバスの描画にすぎません。mCurrentMatchPlayerとonCreateViewHolderの場合は、編集した投稿をチェックアウトしてください。 –

答えて

1

問題はnapkinsterrorが言及しているだけのように(データセットではなくRecyclerViewの下にどのように動作するかの私の理解ではなく、彼の答えで)。

このIT改訂カスタムビュー:

public class KdaBar extends View { 
    private int mKillCount, mDeathCount, mAssistCount; 
    private int mKillColor, mDeathColor, mAssistColor; 
    private int mViewWidth, mViewHeight; 
    private Paint mKillBarPaint, mDeathBarPaint, mAssistBarPaint, mBgPaint; 
    private float mKillPart, mDeathPart, mAssistPart; 

    public KdaBar(Context context, AttributeSet attrs) { 
     super(context, attrs); 

     TypedArray a = context.getTheme().obtainStyledAttributes(
       attrs, 
       R.styleable.KdaBar, 
       0, 0); 

     try { 
      mKillCount = a.getInt(R.styleable.KdaBar_killCount, 0); 
      mDeathCount = a.getInt(R.styleable.KdaBar_deathCount, 0); 
      mAssistCount = a.getInt(R.styleable.KdaBar_assistCount, 0); 

      mKillColor = a.getColor(R.styleable.KdaBar_killBarColor, ContextCompat.getColor(getContext(), R.color.kill_score_color)); 
      mDeathColor = a.getColor(R.styleable.KdaBar_deathBarColor, ContextCompat.getColor(getContext(), R.color.death_score_color)); 
      mAssistColor = a.getColor(R.styleable.KdaBar_assistBarColor, ContextCompat.getColor(getContext(), R.color.assist_score_color)); 
     } finally { 
      a.recycle(); 
     } 

     init(); 
    } 

    public void setValues(int killCount, int deathCount, int assistCount) { 
     mKillCount = killCount; 
     mDeathCount = deathCount; 
     mAssistCount = assistCount; 
    } 

    private void calculatePartitions() { 
     float total = mKillCount + mDeathCount + mAssistCount; 
     mKillPart = (mKillCount/total) * mViewWidth; 
     mDeathPart = (mDeathCount/total) * mViewWidth; 
     mAssistPart = (mAssistCount/total) * mViewWidth; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 

     calculatePartitions(); 

     canvas.drawRect(mKillPart+mDeathPart, 0f, mKillPart+mDeathPart+mAssistPart, mViewHeight, mAssistBarPaint); 
     canvas.drawRect(mKillPart, 0f, mKillPart+mDeathPart, mViewHeight, mDeathBarPaint); 
     canvas.drawRect(0f, 0f, mKillPart, mViewHeight, mKillBarPaint); 
    } 

    @Override 
    protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld){ 
     super.onSizeChanged(xNew, yNew, xOld, yOld); 

     mViewWidth = xNew; 
     mViewHeight = yNew; 
    } 

    private void init() { 
     mKillBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mKillBarPaint.setColor(mKillColor); 

     mDeathBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mDeathBarPaint.setColor(mDeathColor); 

     mAssistBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mAssistBarPaint.setColor(mAssistColor); 

     mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mBgPaint.setColor(ContextCompat.getColor(getContext(), R.color.transparent)); 
    } 
} 

これらは私が行った変更です:onDraw()コールバックが呼び出されるので、親が追加したとき

  1. setValues()内側からinvalidate()コールを削除は、ビュー。
  2. mKillPart,mDeathPartおよびmAssistPartからcalculatePartitions()への割り当てをonDraw()と呼びました。これは、計算に必要な値がonDraw()の内部にあることが保証されているためです。これを以下に説明する。

これは私が氏のnapkinsterrorの答えから収集したものです:

LayoutManagerでビューのためにRecyclerViewを要求すると、最終的には、onBindViewHolder()メソッドが呼び出されます。そのメソッド内では、データはビューにバインドされます。したがって、setValues()が呼び出されます。

ビューはLayoutManagerに返され、次に、アイテムがRecyclerViewに追加されます。ビューの寸法がまだ分かっていないため、このイベントはonSizeChanged()をトリガーします。それでmViewWidthmViewHeightが取得されます。この時点で、calculatePartitions()のすべての必要な値が完成しました。

onDraw()も、親が項目を追加したばかり(imageにチェックを入れているため)とも呼ばれます。 calculatePartitions()onDraw()と呼ばれ、ビューはキャンバス上に問題なく描画されます。

私はmViewWidthmViewHeightが知られるようにまだあるので、非常に、非常に間違っているonSizeChanged()内部calculatePartitions()を行うので、私は前に間違った値を取得する理由があります。

私はこれを答えとしてマークしますが、私が正しい方向に研究できるように資源を提供するためのナプキンストスター。 :)

+0

非常にきれいにやりました。私の「答え」は答えではなく、ちょっとしたポインタとデバッグのヒントであると感じました。私は 'onSizeChanged()'と 'onDraw()'について何も知らなかった。あなたが調査してうれしく思いました。そして、私たちはそれを学びました。何よりもあなたのコードは機能しています! – napkinsterror

2

特にこのコードが別の場所で動作している場合、正確に何が起こっているのかを知るのはかなり難しいですが、私はいくつか考えます。

私が気づいた主なもの:

数字はRecyclerView(特にonBindView) 内部ビューから 無効化を呼び出す
  • 最大
  • に危険なほど近くにある長い
    からint型を比較
    1. 第1号

      あなたの写真では、私はあなたがRecyclerViewのビューホルダーの左下隅にある数字であることを推測しています。たとえば、「2563966339」です。 Androidでは「通常」、Integer.MAX_VALUE = 2147483647を知っている必要があります。これはかなり長いことを意味するもので、長時間使ってみたり、物事が平等ではないと思っているときには... (箱が正しく描かれているかもしれませんが、あなたは気にしないでください。思う?!?!)。

      (詳細については、intとlongの符号付きバイトと使用済みバイトを参照してください)。

      コードを変更する必要がありますが、長いか長いかをお勧めします。

      例1

      long steamId32 = Long.parseLong(mCurrentPlayer.getSteamId()); 
      if (steamId32 == player.getAccountId()) { 
          mCurrentMatchPlayer = player; 
      } 
      

      例以下は、多くの可能性の二つの2

      Long steamId32 = mCurrentPlayer.getSteamId(); 
      if (steamId32.equals(player.getAccountId()) { 
          mCurrentMatchPlayer = player; 
      } 
      

      問題2:

      RecyclerViewがどのように動作するかの理解の欠如があるかもしれませんいくつかの問題を引き起こします。 onBindViewでは、できるだけビューを設定し描画する必要があります(invalidate()を呼び出さずに)。これは、RecyclerViewはすべての「リサイクル」を処理するためのものです。だからあなたはinvalidate()コールがいくつかの奇妙な問題を引き起こしているかもしれない。

      私が知っているのは、ビューがバインドされるたびにonDraw()が呼び出されるのではなく、RecyclerViewを使って作成するときだけです。これは他の場所で働いた理由を説明するでしょう!

      概要と分析:

      ナンバー1:

      私は

      Log.d("Whatever", "At position: " + position + " we have " + <steamId> + <kills> + <other desired info>)setValuesonBindView内)を呼び出します。

      上下にスクロールすると、トップにある人物と呼び出されている値が表示され、#1で述べた問題か自分の位置に問題があるかどうかを確認できます。その人物が0を持つ必要がある場合は、位置0に0を表示させます。私はまだmCurrentPlayerが問題を引き起こす可能性があり、正確であるかわからない

      また、これは私がように可能性が高かったとは思いませんでしたこれらの問題の一つ、間違いなく可能性を指摘することができます。また、アダプターの 'item'を更新する必要がある場合は、Activity/Fragment with recyclerViewからmAdapter.updateItemAt(position)を呼び出してください。移動する必要がある場合はmAdapter.notifyItemMoved(fromPos, toPos)に電話してください。これらはすべて、onBindViewが呼び出されているときに思うものではないことを意味します。

      ナンバー2:

      私はあなたがそれが呼び出されている実際ときに知っている、とだけinvalidate()後にそれを期待していないかどうかを確認するためにonDraw()にもログ文を置くことをお勧めします。ほとんどの場合invaidate()onDraw()を呼び出すことを決定するまでのメインスレッド/リサイクルビューでキューされています。

      (ので、すでにcreated/drewonCreateView()の項目)

      あなたがRecyclerView、LayoutManagerの、およびアダプタが何をするかによって驚くかもしれませんし、それらがどのように表示するメソッドを呼び出します。(onBindViewonCreateViewのログステートメントをonDraw()で処理全体を理解しておきたい場合もあります)。

      理解RecyclerView(と、それは部品だ)

      ビデオの基礎を学ぶために:

      そして読者のために、Androidのドキュメントは、この要約を提供しました:

      Adapter: A subclass of RecyclerView.Adapter responsible for providing views that represent items in a data set. 
      Position: The position of a data item within an Adapter. 
      Index: The index of an attached child view as used in a call to getChildAt(int). Contrast with Position. 
      Binding: The process of preparing a child view to display data corresponding to a position within the adapter. 
      Recycle (view): A view previously used to display data for a specific adapter position may be placed in a cache for later reuse to display the same type of data again later. This can drastically improve performance by skipping initial layout inflation or construction. 
      Scrap (view): A child view that has entered into a temporarily detached state during layout. Scrap views may be reused without becoming fully detached from the parent RecyclerView, either unmodified if no rebinding is required or modified by the adapter if the view was considered dirty. 
      Dirty (view): A child view that must be rebound by the adapter before being displayed. 
      
    +0

    1.希望の値が数値の下位32ビットに含まれているため、SteamIdをlongから整数にキャストしようとしていますが、これは何とか安全ではないことに同意します。それ以外には、私は値が正しいと確信して、彼らが必要としていた位置にいます。 2. RecyclerViewがどのように動作するかに関してあなたが言ったことのほぼ半分は、私にとって全く新しいものです。これらのヒントをありがとう、私はこれらを試して間違いなく行くつもりです。進行/改善があれば更新されます。 また、実際にはこれを答えとしてマークします。ありがとうございました:) –

    +0

    Number 1とNumber 2の両方がより明確になるように更新されました。 'onBindView'と' onCreateView'がどのように動作するかを確実に読んでください。また、他の人がキャンバスに描いている他の例を、recyclerviewの中に見つけることができるかどうかを知りたいかもしれません。私はそれが 'invalidate()'を呼び出さないという気持ちがある。がんばろう。 – napkinsterror

    関連する問題