2014-03-29 69 views
0

基本上,我想继承ClipData.Item的子类,以便我可以发送除CharSequence,IntentURI以外的数据以及DragEvent。文档似乎暗示这是可能的(请参阅documentation以及toString()方法,其中特别提到了Item的子类),但是,尽管ClipDataItem均未被声明为final,但我尝试过的所有内容都无法正常工作。是否可以继承android.content.ClipData或ClipData.Item?

,我已经得到了基本设置是一个内部类,像这样延伸ClipData.Item

TowerButton.java

package com.conundrum.toweroffensenative.app; 

import android.content.ClipData; 
import android.content.ClipDescription; 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.graphics.Rect; 
import android.graphics.RectF; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.View; 

import java.util.List; 

/** 
* Created by Nums on 27/03/14. 
*/ 
public class TowerButton extends View { 
    private Paint mBackgroundPaint, mTowerPaint, mShadowPaint, mLabelPaint, mDisabledPaint; 
    private List<Tower> mTowers; 
    private Path mTowerPath; 
    private DragShadowBuilder mDragShadowBuilder; 

    private Rect r; 

    public TowerButton(Context context, AttributeSet attrs, List<Tower> towers) { 
     super(context, attrs); 

     mTowers = towers; 
     init(); 
    } 

    // If I need a tower type that starts with 0 stock, add constructor which takes Paint/Path as args 
    private void init() { 
     mBackgroundPaint = new Paint(); 
     mBackgroundPaint.setStyle(Paint.Style.FILL); 
     mBackgroundPaint.setColor(Color.GRAY); 

     mTowerPaint = mTowers.get(0).getPaint(); 
     mTowerPath = mTowers.get(0).getPath(); 

     mShadowPaint = new Paint(mTowerPaint); 
     mShadowPaint.setAlpha(150); 

     mDisabledPaint = new Paint(mTowerPaint); 
     mDisabledPaint.setColor(Color.LTGRAY); 
     mDisabledPaint.setAlpha(150); 

     mLabelPaint = new Paint(); 
     mLabelPaint.setTextSize(28); 
     mLabelPaint.setTextAlign(Paint.Align.CENTER); 
     mLabelPaint.setAntiAlias(true); 
     mLabelPaint.setColor(Color.WHITE); 

     mDragShadowBuilder = new DragShadowBuilder(this) { 
      @Override 
      public void onDrawShadow(Canvas canvas) { 
       canvas.drawPath(mTowerPath, mShadowPaint); 
      } 
     }; 

     setTag(mTowers.get(0).getClass().getName() + "Button"); 

     r = new Rect(); 
    } 

    public String getQuantity() { 
     return String.valueOf(mTowers.size()); 
    } 

    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
     super.onSizeChanged(w, h, oldw, oldh); 

     Matrix pathMatrix = new Matrix(); 
     RectF pathBounds = new RectF(); 
     mTowerPath.computeBounds(pathBounds, false); 
     pathMatrix.setScale(w/pathBounds.width(), h/pathBounds.height()); 
     mTowerPath.transform(pathMatrix); 

     r.set(0, 0, w, h); 
    } 

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

     canvas.drawRect(r, mBackgroundPaint); 

     if (mTowers.size() > 0) { 
      canvas.drawPath(mTowerPath, mTowerPaint); 
      canvas.drawText(getQuantity(), getX() + (getWidth()/2), getY() + (getHeight()/2), mLabelPaint); 
     } else { 
      canvas.drawPath(mTowerPath, mDisabledPaint); 
     } 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     if (event.getAction() == MotionEvent.ACTION_DOWN && mTowers.size() > 0) { 
      Tower dragTower = mTowers.get(0); 
      TowerItem item = new TowerItem(dragTower); 
      ClipData dragData = new ClipData(dragTower.getBuildRow(), 
        new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN }, item); 

      startDrag(dragData, mDragShadowBuilder, null, 0); 
      return true; 
     } 
     return false; 
    } 

    public Tower giveTower() { 
     // TODO: need checking to ensure size > 0? 
     Tower tower = mTowers.remove(0); 
     invalidate(); 
     return tower; 
    } 

    public void recycleTower(Tower tower) { 
     mTowers.add(tower); 
     invalidate(); 
    } 

    public static class TowerItem extends ClipData.Item { 
     final Tower mTower; 

     public TowerItem(Tower tower) { 
      super(""); 
      mTower = tower; 
     } 

     public Tower getTower() { 
      return mTower; 
     } 

     @Override 
     public CharSequence coerceToText(Context context) { 
      if (mTower != null) { 
       return mTower.getClass().getName(); 
      } 
      return super.coerceToText(context); 
     } 
    } 
} 

然后,在类将接受DropEvent

TowerView.java

package com.conundrum.toweroffensenative.app; 

import android.animation.Animator; 
import android.animation.AnimatorListenerAdapter; 
import android.content.ClipData; 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.LinearGradient; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.graphics.Rect; 
import android.graphics.RectF; 
import android.graphics.Shader; 
import android.os.Build; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.DragEvent; 
import android.view.GestureDetector; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.ViewGroup; 
import android.view.ViewPropertyAnimator; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.Callable; 

/** 
* Created by Nums on 24/03/14. 
*/ 
public class TowerView extends View { 
    private Paint mBasePaint, mHighlightPaint, mStunnedPaint, mSelectedPaint; 

    private Tower mTower; 
    private Path mTowerPath; 
    private Paint mTowerPaint; 

    private boolean highlighted; 
    private boolean stunned; 
    private boolean selected; 

    private int mIndex; 
    private List<TowerView> mNeighbours; 

    private Rect r; 

    private class mListener extends GestureDetector.SimpleOnGestureListener { 
     @Override 
     public boolean onDown(MotionEvent e) { 
      return true; 
     } 

     @Override 
     public boolean onSingleTapUp(MotionEvent e) { 
      for (TowerView tv : mNeighbours) { 
       tv.highlighted ^= true; 
       tv.invalidate(); 
      } 
      return true; 
     } 

     @Override 
     public void onLongPress(MotionEvent e) { 
      List<TowerView> myRow = ((TowerGrid) getParent()).getRow(mIndex % TowerGrid.ROWS); 
      for (TowerView v : myRow) { 
       v.stunned ^= true; 
       v.invalidate(); 
      } 
     } 
    } 
    GestureDetector mDetector = new GestureDetector(TowerView.this.getContext(), new mListener()); 

    Callable<Void> mStartRecycleCallable = new Callable<Void>() { 
     @Override 
     public Void call() throws Exception { 
      startRecycle(); 
      return null; 
     } 
    }; 

    Callable<Void> mRecycleCallable = new Callable<Void>() { 
     @Override 
     public Void call() throws Exception { 
      recycle(); 
      return null; 
     } 
    }; 

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

    private void init() { 
     mIndex = -1; 
     mNeighbours = new ArrayList<TowerView>(); 

     highlighted = false; 
     stunned = false; 
     selected = false; 

     LinearGradient baseGradient = new LinearGradient(0, 0, 0, 25, 
       new int[] {Color.LTGRAY, Color.DKGRAY}, null, Shader.TileMode.MIRROR); 
     LinearGradient highlightGradient = new LinearGradient(0, 0, 0, 25, 
       new int[] {Color.YELLOW, Color.RED}, null, Shader.TileMode.MIRROR); 
     LinearGradient stunnedGradient = new LinearGradient(0, 0, 0, 25, 
       new int[] {Color.CYAN, Color.BLUE}, null, Shader.TileMode.MIRROR); 

     mBasePaint = new Paint(); 
     mBasePaint.setShader(baseGradient); 

     mHighlightPaint = new Paint(); 
     mHighlightPaint.setShader(highlightGradient); 

     mStunnedPaint = new Paint(); 
     mStunnedPaint.setShader(stunnedGradient); 

     mSelectedPaint = new Paint(); 
     mSelectedPaint.setStyle(Paint.Style.STROKE); 
     mSelectedPaint.setColor(Color.GREEN); 
     mSelectedPaint.setStrokeWidth(5); 

     r = new Rect(); 
    } 

    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
     super.onSizeChanged(w, h, oldw, oldh); 

     r.set(0, 0, w, h); 
    } 

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

     // Draw the tower base in one of three styles 
     if (highlighted) { 
      canvas.drawRect(r, mHighlightPaint); 
     } else if (stunned) { 
      canvas.drawRect(r, mStunnedPaint); 
     } else { 
      canvas.drawRect(r, mBasePaint); 
     } 

     if (mTower != null) { 
      canvas.drawPath(mTowerPath, mTowerPaint); 
     } 

     if (selected) { 
      canvas.drawRect(r, mSelectedPaint); 
     } 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     boolean result = mDetector.onTouchEvent(event); 
     if (!result) { 
      // Custom gesture code 
     } 
     return result; 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent event) { 
     boolean result = super.dispatchTouchEvent(event); 
     return result; 
    } 

    @Override 
    public boolean onDragEvent(DragEvent event) { 
     final int action = event.getAction(); 
     switch(action) { 
      case DragEvent.ACTION_DRAG_STARTED: 
       // check if Tower can be built on this col - in case I allow that to differ per Tower 
       if (mIndex/TowerGrid.ROWS == Integer.parseInt(event.getClipDescription().getLabel().toString())) { 
        selected = true; 
        invalidate(); 
        return true; 
       } 
       return false; 
      case DragEvent.ACTION_DRAG_ENTERED: 
       highlighted = true; 
       invalidate(); 
       break; 
      case DragEvent.ACTION_DRAG_EXITED: 
       highlighted = false; 
       invalidate(); 
       break; 
      case DragEvent.ACTION_DROP: 
       ClipData.Item item = event.getClipData().getItemAt(0); 
       if (item instanceof TowerButton.TowerItem) { 
        Log.d("towerview", "SUCCESS!"); 
       } 
       // Always returns false 
       TowerButton.TowerItem tItem = (TowerButton.TowerItem) item; // exception 
       Tower dragTower = item.getTower(); 
       setTower(dragTower); 

       return true; 
      case DragEvent.ACTION_DRAG_ENDED: 
       highlighted = false; 
       selected = false; 
       invalidate(); 
       return true; 
     } 
     return false; 
    } 

    public void setTower(Tower tower) { 
     if (mTower != null) { 
      TowerButton button = (TowerButton) getRootView().findViewWithTag(mTower.getClass().getName() + "Button"); 
      button.recycleTower(mTower); 
     } 
     mTower = tower; 
     mTowerPaint = tower.getPaint(); 
     mTowerPath = tower.getPath(); 

     Matrix pathMatrix = new Matrix(); 
     RectF pathBounds = new RectF(); 
     mTowerPath.computeBounds(pathBounds, false); 
     pathMatrix.setScale(getWidth()/pathBounds.width(), getHeight()/pathBounds.height()); 
     mTowerPath.transform(pathMatrix); 
     invalidate(); 
    } 

    public boolean advance(int distance) { 
     if (!stunned) { 
      // first account for the new view being added 
      setTranslationX(getTranslationX() - distance); 
      // then animate right over 1000 ms 
      ViewPropertyAnimator animator = animate().translationXBy(distance).setDuration(1000); 
      addCompatibilityAnimationCallback(animator, mStartRecycleCallable).start(); 
      return true; 
     } 
     return false; 
    } 

    private void startRecycle() { 
     if (mIndex/TowerGrid.ROWS == TowerGrid.COLS - 1) { 
      ViewPropertyAnimator animator = animate().translationXBy(getWidth()/-2).scaleX(0).setDuration(1000); 
      addCompatibilityAnimationCallback(animator, mRecycleCallable).start(); 
     } 
    } 

    private void recycle() { 
     if (mTower != null) { 
      TowerButton button = (TowerButton) getRootView().findViewWithTag(mTower.getClass().getName() + "Button"); 
      button.recycleTower(mTower); 
     } 
     ((ViewGroup) getParent()).removeView(this); 
    } 

    public void updateNeighbours() { 
     ViewGroup parent = (ViewGroup) getParent(); 
     mIndex = parent.indexOfChild(this); 
     mNeighbours.clear(); 
     if (mIndex >= TowerGrid.ROWS) { 
      mNeighbours.add((TowerView) parent.getChildAt(mIndex - TowerGrid.ROWS)); 
     } 
     if (mIndex < TowerGrid.ROWS * (TowerGrid.COLS - 2)) { 
      mNeighbours.add((TowerView) parent.getChildAt(mIndex + TowerGrid.ROWS)); 
     } 
     if (mIndex % TowerGrid.ROWS != 0) { 
      mNeighbours.add((TowerView) parent.getChildAt(mIndex - 1)); 
     } 
     if (mIndex % TowerGrid.ROWS != TowerGrid.ROWS - 1) { 
      mNeighbours.add((TowerView) parent.getChildAt(mIndex + 1)); 
     } 
    } 

    private ViewPropertyAnimator addCompatibilityAnimationCallback(ViewPropertyAnimator animator, final Callable<Void> callbackFunc) { 
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 
      animator.setListener(new AnimatorListenerAdapter() { 
       @Override 
       public void onAnimationEnd(Animator animation) { 
        try { 
         callbackFunc.call(); 
        } catch (Exception e) { 
         e.printStackTrace(); 
        } 
       } 
      }); 
     } else { 
      animator.withEndAction(new Runnable() { 
       @Override 
       public void run() { 
        try { 
         callbackFunc.call(); 
        } catch (Exception e) { 
         e.printStackTrace(); 
        } 
       } 
      }); 
     } 
     return animator; 
    } 
} 

没有编译时错误。但是,试图在运行时执行转换的时候,我得到异常:

java.lang.ClassCastException: android.content.ClipData$Item cannot be cast to com.conundrum.toweroffensenative.app.TowerButton$TowerItem

同样,类似的代码item instanceof TowerButton.TowerItem返回false,即使TowerItem明显延长ClipData.Item

有什么我失踪,阻止这些类被分类?或者我做错了什么?我知道我可以使用ContentProviderURI来传输更复杂的信息,但是当传输的数据永远不必在此应用程序之外提供时,这看起来似乎过度杀伤。

编辑

我也试着制作ClipData一个子类,它有自己Item内部类,这样我可以覆盖getItem()返回一个TowerItem - 但是我需要投了ClipDataTowerClipData ,失败时会出现相同的错误。

编辑3

既包括相关文件的全部。

回答

0

TowerItemClipData.Item后裔,因此TowerItemClipData.Item,但相反并不总是如此。 A ClipData.Item可以是TowerItem,但不一定。

您通过明确铸造至TowerItem来避免编译器错误:(TowerItem)ClipData.Item。但是你不能避免运行时错误。

instanceOf正确用法应该是这样:

if(event.getClipData().getItemAt(0) instanceOf TowerButton.TowerItem) { 
    TowerButton.TowerItem item = (TowerButton.TowerItem) event.getClipData().getItemAt(0); 
} 
+0

感谢蒂姆,但与该代码,'event.getClipData()。getItemAt(0)instanceof TowerButton.TowerItem'永远不会返回true。这是有道理的,因为我在'onTouchEvent'中指定'ClipData'。我编辑了我的答案以包含该代码,因此您可以看到'getItemAt(0)'应该始终是我班的实例。 –

+0

您必须将TowerItem放入ClipData以便能够在稍后获得并成功将其转换为TowerItem。 –

+0

我有 - 看看**编辑2 **后的代码。我实例化一个'TowerItem',并将它传递给'ClipData'的构造函数。 –

0

类似的问题,我已经打算在这个系统中的代码。基本上,ClipData是一个Parcelable,所以我认为ClipData.Item的一个子类没有一个明智的ClipData子类,它可以识别你的自定义ClipData.Item将被转换为Charsequence,然后作为基本文本返回ClipData.Item。否则,您需要为URI格式创建一个ContentProvider,对于这种用例,在单个应用程序中使用ui会造成过度杀伤,甚至可能是错误的。 对此我的破解最终将ClipData中的标识标签作为文本传递,并将拖动初始化视图(通过状态传递给事件数据)负责将其转换为对象。不完全干净但不完全难看。

相关问题