Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/377.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jquery/87.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 是否可以将android.content.ClipData或ClipData.Item子类化?_Java_Android_Inheritance - Fatal编程技术网

Java 是否可以将android.content.ClipData或ClipData.Item子类化?

Java 是否可以将android.content.ClipData或ClipData.Item子类化?,java,android,inheritance,Java,Android,Inheritance,基本上,我想对ClipData.Item进行子类化,这样我就可以发送除CharSequence、Intent或URI之外的数据以及DragEvent。文档似乎暗示这是可能的(参见toString()方法旁边的,它特别提到了Item的子类),但是,尽管ClipData和Item都没有被声明为final,但我尝试的一切都没有起作用 我得到的基本设置是一个内部类,它扩展了ClipData.Item,如下所示: TowerButton.java package com.conundrum.towerof

基本上,我想对
ClipData.Item
进行子类化,这样我就可以发送除
CharSequence
Intent
URI
之外的数据以及
DragEvent
。文档似乎暗示这是可能的(参见
toString()
方法旁边的,它特别提到了
Item
的子类),但是,尽管
ClipData
Item
都没有被声明为
final
,但我尝试的一切都没有起作用

我得到的基本设置是一个内部类,它扩展了
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);
        }
    }
}
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不能强制转换为com.conundrum.towerofensenative.app.TowerButton$TowerItem

类似地,类似于TowerButton.TowerItem的
item instanceof TowerButton.TowerItem
这样的代码返回false,即使
TowerItem
显然在扩展
ClipData.item

我是否缺少一些东西来阻止这些类被子类化?还是我做错了什么?我知道我可以使用
ContentProvider
URI
s来传输更复杂的信息,但当传输的数据不必在该应用程序之外可用时,这似乎有些过分

编辑

我还尝试使用自己的
Item
内部类来创建
ClipData
的子类,这样我就可以重写
getItem()
来返回
TowerItem
——但是接下来我需要将
ClipData
强制转换为
TowerClipData
,但同样的错误也会导致失败

编辑3


包含两个相关文件的全部。

TowerItem
是从
ClipData.Item
派生而来,因此
TowerItem
ClipData.Item
,但反过来并不总是正确的。
ClipData.Item
可以是
TowerItem
,但不一定是

通过显式地将
ClipData.Item
强制转换为
TowerItem
(TowerItem)ClipData.Item
,可以避免编译器错误。但您无法避免运行时错误

instanceOf
的正确用法如下:

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

类似的问题,我已经检查了这个的系统代码。基本上,ClipData是一个可包裹的,因此我认为ClipData.Item的一个子类没有识别自定义ClipData的合理ClipData子类。Item将转换为字符序列,然后作为基本文本ClipData.Item返回。否则,您需要为URI格式创建一个ContentProvider,在本用例中,该格式是单个应用程序中的ui,这是一种过分的做法,可能是错误的。
我的技巧是最终将ClipData中的标识标记作为文本传递,并使拖动启动视图(通过状态传递到事件数据中)负责将其转化为对象。不是很干净,但也不是很难看。

谢谢Tim,但是有了这段代码,
event.getClipData().getItemAt(0)instanceof TowerButton.TowerItem
永远不会返回true。这是有意义的,因为
ClipData
是由我在
onTouchEvent
中指定的。我已经编辑了我的答案以包含该代码,因此您可以看到
getItemAt(0)
应该始终是我的类的一个实例。您必须将TowerItem放入ClipData中,以便以后能够获取并成功地将其转换为TowerItem。我已经-在编辑2后查看代码。我实例化一个
TowerItem
并将其传递给
ClipData
的构造函数。您能显示相关代码的整个部分吗?我想知道你是如何和在哪里声明你的类的。好的,我现在已经把这两个文件都放在那里了。java相当长——我想删掉一些,但我想最好还是把它全部留下,以防删掉相关内容。