有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java是安卓的子类。所容纳之物ClipData或ClipData。项目

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

我得到的基本设置是一个扩展ClipData.Item的内部类,如下所示:

TowerButton.java

package com.conundrum.toweroffensenative.app;

import 安卓.content.ClipData;
import 安卓.content.ClipDescription;
import 安卓.content.Context;
import 安卓.graphics.Canvas;
import 安卓.graphics.Color;
import 安卓.graphics.Matrix;
import 安卓.graphics.Paint;
import 安卓.graphics.Path;
import 安卓.graphics.Rect;
import 安卓.graphics.RectF;
import 安卓.util.AttributeSet;
import 安卓.view.MotionEvent;
import 安卓.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 安卓.animation.Animator;
import 安卓.animation.AnimatorListenerAdapter;
import 安卓.content.ClipData;
import 安卓.content.Context;
import 安卓.graphics.Canvas;
import 安卓.graphics.Color;
import 安卓.graphics.LinearGradient;
import 安卓.graphics.Matrix;
import 安卓.graphics.Paint;
import 安卓.graphics.Path;
import 安卓.graphics.Rect;
import 安卓.graphics.RectF;
import 安卓.graphics.Shader;
import 安卓.os.Build;
import 安卓.util.AttributeSet;
import 安卓.util.Log;
import 安卓.view.DragEvent;
import 安卓.view.GestureDetector;
import 安卓.view.MotionEvent;
import 安卓.view.View;
import 安卓.view.ViewGroup;
import 安卓.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: 安卓.content.ClipData$Item cannot be cast to com.conundrum.toweroffensenative.app.TowerButton$TowerItem

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

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

编辑

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

编辑3

包括两个相关文件的全部内容


共 (2) 个答案

  1. # 1 楼答案

    TowerItemClipData.Item的后代,因此TowerItemClipData.Item,但反过来并不总是正确的。A ClipData.Item可以是A 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);
    }
    
  2. # 2 楼答案

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