2014-03-29 73 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中的標識標籤作爲文本傳遞,並將拖動初始化視圖(通過狀態傳遞給事件數據)負責將其轉換爲對象。不完全乾淨但不完全難看。

相關問題