Android Programming: Pushing the Limits -- Chapter 5: Android User Interface Operations

多屏幕
自定义View

多屏幕

@、Android 4.2 开始支持多屏幕。

@、举例:

 

public class SecondDisplayDemo extends Activity {

    private Presentation mPresentation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.device_screen);
    }

    @Override
    protected void onResume() {
        super.onResume();
        setupSecondDisplay();
    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mPresentation != null){
            mPresentation.cancel();
        }
    }

    private void setupSecondDisplay(){
        DisplayManager displayManager = (DisplayManager)
                getSystemService(Context.DISPLAY_SERVICE);
        Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
        Display[] presentationDisplays = displayManager
                .getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
        if(presentationDisplays.length > 0){
            for(Display presentationDisplay : presentationDisplays){
                if(presentationDisplay.getDisplayId() != defaultDisplay.getDisplayId()){
                    Presentation presentation =
                            new MyPresentation(this, presentationDisplay);
                    presentation.show();
                    mPresentation = presentation;
                    return;
                }
            }
        }
        Toast.makeText(this, "No second display found!", Toast.LENGTH_SHORT).show();
    }

    private class MyPresentation extends Presentation{
        public MyPresentation(Context context, Display display){
            super(context, display);
            // The View for the second screen
            setContentView(R.layout.second_screen);
        }
    }
}
SecondDisplayDemo.java

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".SecondDisplayDemo">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:textSize="32sp"
        android:text="@string/first_screen" />

</RelativeLayout>
device_screen.xml

 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:paddingLeft="@dimen/activity_horizontal_margin"
              android:paddingRight="@dimen/activity_horizontal_margin"
              android:paddingTop="@dimen/activity_vertical_margin"
              android:paddingBottom="@dimen/activity_vertical_margin"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_gravity="center"
              android:gravity="center"
              android:text="@string/second_screen_content"
              android:textSize="32sp" />

</LinearLayout>
second_screen.xml

 

 

自定义View:

 

@、在onAttachedToWindow中加载资源,初始化数据,但最好与大小、位置无关的数据,因为onAttachedToWindow可能在onMeasure之前或之后被调用,这时候可能还不知道view的height和width。

@、View的绘制有两步:a measure pass and a layout pass。

https://developer.android.com/guide/topics/ui/how-android-draws.html

1、  通过measure(int, int)方法确定个组件的height和width,此方法调用可能不止一次,直到所有组件都确定好。此方法会调用onMeasure()。此方法不可被子类覆盖,子类应该重写的是onMeasure()。

2、  layout(int, int, int, int)通过上一步获取的height和width布局子控件。子类不要覆盖此方法而是onLayout方法。在onLayout方法中可调用子控件的layout方法。

@、onLayout处理跟大小、位置相关的数据。

@、onDraw只专注处理绘制工作,而不要有繁重的计算工作。

@、onTouchEvent处理触摸事件。

@、MotionEvent

1、   Each complete gesture is represented by a sequence of motion events with actions that describe pointer state transitions and movements. A gesture starts with a motion event with ACTION_DOWN that provides the location of the first pointer down. As each additional pointer that goes down or up, the framework will generate a motion event with ACTION_POINTER_DOWN or ACTION_POINTER_UP accordingly. Pointer movements are described by motion events with ACTION_MOVE. Finally, a gesture end either when the final pointer goes up as represented by a motion event with ACTION_UP or when gesture is canceled with ACTION_CANCEL.

2、  getActionIndex:获取pointer的下标,简单理解在MotionEvent中包含一个pointer的数组,当发生ACTION_UP或ACTION_POINTER_UP事件时,相当于从数组中删除一个数据,这样pointer的下标就有可能变化。如果是新的pointer,则新pointer的下标为最小空闲下标;如果是原有的pointer,则此pointer的新下标为原下标减去它前面空闲下标的数量。

3、  getPointerId:获取pointer的标识符,标识符不会改变,而下标是会改变的。

4、  findPointerIndex:根据标识符找pointer下标,如果pointer已经失效了则返回-1。

5、  ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_CANCEL, ACTION_OUTSIDE, ACTION_POINTER_DOWN, ACTION_POINTER_UP.

@、PointerCoords:存放pointer coordinates数据。(这些数据可以通过MotionEvent类获取吧,那这个类主要作用是什么?传递时少一些数据吗?)

@、通过Canvas的rotate方法实现旋转。

@、多点触摸实例:

例一:PianoKeyBoard

 

public class PianoKeyboard extends View {
    public static final String LOG_TAG = "PianoKeyboard";
    public static final int MAX_FINGERS = 5;
    public static final int WHITE_KEYS_COUNT = 7;
    public static final int BLACK_KEYS_COUNT = 5;
    public static final float BLACK_TO_WHITE_WIDTH_RATIO = 0.625f;
    public static final float BLACK_TO_WHITE_HEIGHT_RATIO = 0.54f;
    private Paint mWhiteKeyPaint, mBlackKeyPaint, mBlackKeyHitPaint, mWhiteKeyHitPaint;
    // Support up to five fingers
    private Point[] mFingerPoints = new Point[MAX_FINGERS];
    private int[] mFingerTones = new int[MAX_FINGERS];
    private SoundPool mSoundPool;
    private SparseIntArray mToneToIndexMap = new SparseIntArray();
    private Paint mCKeyPaint, mCSharpKeyPaint, mDKeyPaint,
            mDSharpKeyPaint, mEKeyPaint, mFKeyPaint,
            mFSharpKeyPaint, mGKeyPaint, mGSharpKeyPaint,
            mAKeyPaint, mASharpKeyPaint, mBKeyPaint;
    private Rect mCKey = new Rect(), mCSharpKey = new Rect(),
            mDKey = new Rect(), mDSharpKey = new Rect(),
            mEKey = new Rect(), mFKey = new Rect(),
            mFSharpKey = new Rect(), mGKey = new Rect(),
            mGSharpKey = new Rect(), mAKey = new Rect(),
            mASharpKey = new Rect(), mBKey = new Rect();
    private MotionEvent.PointerCoords mPointerCoords;

    public PianoKeyboard(Context context) {
        super(context);
    }

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

    public PianoKeyboard(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onAttachedToWindow() {
        Log.d(LOG_TAG, "In onAttachedToWindow");
        super.onAttachedToWindow();
        mPointerCoords = new MotionEvent.PointerCoords();
        Arrays.fill(mFingerPoints, null);
        Arrays.fill(mFingerTones, -1);
        loadKeySamples(getContext());
        setupPaints();
    }

    @Override

    protected void onDetachedFromWindow() {
        Log.d(LOG_TAG, "In onDetachedFromWindow");
        super.onDetachedFromWindow();
        releaseKeySamples();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d(LOG_TAG, "In onMeasure");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        Log.d(LOG_TAG, "In onLayout");
        super.onLayout(changed, left, top, right, bottom);
        int width = getWidth();
        int height = getHeight();
        int whiteKeyWidth = width / WHITE_KEYS_COUNT;
        int blackKeyWidth = (int) (whiteKeyWidth * BLACK_TO_WHITE_WIDTH_RATIO);
        int blackKeyHeight = (int) (height * BLACK_TO_WHITE_HEIGHT_RATIO);
        mCKey.set(0, 0, whiteKeyWidth, height);
        mCSharpKey.set(whiteKeyWidth - (blackKeyWidth / 2), 0,
                whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mDKey.set(whiteKeyWidth, 0, 2 * whiteKeyWidth, height);
        mDSharpKey.set(2 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                2 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mEKey.set(2 * whiteKeyWidth, 0, 3 * whiteKeyWidth, height);
        mFKey.set(3 * whiteKeyWidth, 0, 4 * whiteKeyWidth, height);
        mFSharpKey.set(4 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                4 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mGKey.set(4 * whiteKeyWidth, 0, 5 * whiteKeyWidth, height);
        mGSharpKey.set(5 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                5 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mAKey.set(5 * whiteKeyWidth, 0, 6 * whiteKeyWidth, height);
        mASharpKey.set(6 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                6 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mBKey.set(6 * whiteKeyWidth, 0, 7 * whiteKeyWidth, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.d(LOG_TAG, "In onDraw");
        super.onDraw(canvas);

        canvas.drawRect(mCKey, mCKeyPaint);
        canvas.drawRect(mDKey, mDKeyPaint);
        canvas.drawRect(mEKey, mEKeyPaint);
        canvas.drawRect(mFKey, mFKeyPaint);
        canvas.drawRect(mGKey, mGKeyPaint);
        canvas.drawRect(mAKey, mAKeyPaint);
        canvas.drawRect(mBKey, mBKeyPaint);

        canvas.drawRect(mCSharpKey, mCSharpKeyPaint);
        canvas.drawRect(mDSharpKey, mDSharpKeyPaint);
        canvas.drawRect(mFSharpKey, mFSharpKeyPaint);
        canvas.drawRect(mGSharpKey, mGSharpKeyPaint);
        canvas.drawRect(mASharpKey, mASharpKeyPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(LOG_TAG, "In onTouchEvent");
        int pointerCount = event.getPointerCount();
        Log.d(LOG_TAG, "In onTouchEvent pointerCount = " + pointerCount);
        int cappedPointerCount = pointerCount > MAX_FINGERS ? MAX_FINGERS : pointerCount;
        Log.d(LOG_TAG, "In onTouchEvent cappedPointerCount = " + cappedPointerCount);
        int actionIndex = event.getActionIndex();
        Log.d(LOG_TAG, "In onTouchEvent actionIndex = " + actionIndex);
        int action = event.getActionMasked();
        Log.d(LOG_TAG, "In onTouchEvent action = " + action);
        int id = event.getPointerId(actionIndex);
        Log.d(LOG_TAG, "In onTouchEvent id = " + id);

        if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) && id < MAX_FINGERS) {
            mFingerPoints[id] = new Point((int) event.getX(actionIndex), (int) event.getY(actionIndex));
        } else if ((action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_UP) && id < MAX_FINGERS) {
            mFingerPoints[id] = null;
            invalidateKey(mFingerTones[id]);
            mFingerTones[id] = -1;
        }

        for (int i = 0; i < cappedPointerCount; i++) {
            int index = event.findPointerIndex(i);
            if (mFingerPoints[i] != null && index != -1) {
                mFingerPoints[i].set((int) event.getX(index), (int) event.getY(index));
                int tone = getToneForPoint(mFingerPoints[i]);
                invalidateKey(1);
                if (tone != mFingerTones[i] && tone != -1) {
                    invalidateKey(mFingerTones[i]);
                    mFingerTones[i] = tone;
                    invalidateKey(mFingerTones[i]);
                    if (!isKeyDown(i)) {
                        int poolIndex = mToneToIndexMap.get(mFingerTones[i]);
                        event.getPointerCoords(index, mPointerCoords);
                        float volume = mPointerCoords.getAxisValue(MotionEvent.AXIS_PRESSURE);
                        volume = volume > 1f ? 1f : volume;
                        mSoundPool.play(poolIndex, volume, volume, 0, 0, 1f);
                    }
                }
            }
        }

        updatePaints();

        return true;
    }

    private void setupPaints() {
        mWhiteKeyPaint = new Paint();
        mWhiteKeyPaint.setStyle(Paint.Style.STROKE);
        mWhiteKeyPaint.setColor(Color.BLACK);
        mWhiteKeyPaint.setStrokeWidth(3);
        mWhiteKeyPaint.setAntiAlias(true);
        mCKeyPaint = mWhiteKeyPaint;
        mDKeyPaint = mWhiteKeyPaint;
        mEKeyPaint = mWhiteKeyPaint;
        mFKeyPaint = mWhiteKeyPaint;
        mGKeyPaint = mWhiteKeyPaint;
        mAKeyPaint = mWhiteKeyPaint;
        mBKeyPaint = mWhiteKeyPaint;

        mWhiteKeyHitPaint = new Paint(mWhiteKeyPaint);
        mWhiteKeyHitPaint.setColor(Color.LTGRAY);
        mWhiteKeyHitPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        mBlackKeyPaint = new Paint();
        mBlackKeyPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mBlackKeyPaint.setColor(Color.BLACK);
        mBlackKeyPaint.setAntiAlias(true);
        mCSharpKeyPaint = mBlackKeyPaint;
        mDSharpKeyPaint = mBlackKeyPaint;
        mFSharpKeyPaint = mBlackKeyPaint;
        mGSharpKeyPaint = mBlackKeyPaint;
        mASharpKeyPaint = mBlackKeyPaint;

        mBlackKeyHitPaint = new Paint(mBlackKeyPaint);
        mBlackKeyHitPaint.setColor(Color.DKGRAY);
    }

    private void loadKeySamples(Context context) {
        mSoundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
//        mToneToIndexMap.put(R.raw.c, mSoundPool.load(context, R.raw.c, 1));
//        mToneToIndexMap.put(R.raw.c_sharp, mSoundPool.load(context, R.raw.c_sharp, 1));
//        mToneToIndexMap.put(R.raw.d, mSoundPool.load(context, R.raw.d, 1));
//        mToneToIndexMap.put(R.raw.d_sharp, mSoundPool.load(context, R.raw.d_sharp, 1));
//        mToneToIndexMap.put(R.raw.e, mSoundPool.load(context, R.raw.e, 1));
//        mToneToIndexMap.put(R.raw.f, mSoundPool.load(context, R.raw.f, 1));
//        mToneToIndexMap.put(R.raw.f_sharp, mSoundPool.load(context, R.raw.f_sharp, 1));
//        mToneToIndexMap.put(R.raw.g, mSoundPool.load(context, R.raw.g, 1));
//        mToneToIndexMap.put(R.raw.g_sharp, mSoundPool.load(context, R.raw.g_sharp, 1));
//        mToneToIndexMap.put(R.raw.a, mSoundPool.load(context, R.raw.a, 1));
//        mToneToIndexMap.put(R.raw.a_sharp, mSoundPool.load(context, R.raw.a_sharp, 1));
//        mToneToIndexMap.put(R.raw.b, mSoundPool.load(context, R.raw.b, 1));
    }

    public void releaseKeySamples() {
        mToneToIndexMap.clear();
        mSoundPool.release();
    }

    private boolean isKeyDown(int finger) {
        int key = getToneForPoint(mFingerPoints[finger]);

        for (int i = 0; i < mFingerPoints.length; i++) {
            if (i != finger) {
                Point fingerPoint = mFingerPoints[i];
                if (fingerPoint != null) {
                    int otherKey = getToneForPoint(fingerPoint);
                    if (otherKey == key) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    private void invalidateKey(int tone) {
        invalidate(mCKey);
        switch (tone) {
//            case R.raw.c:
//                invalidate(mCKey);
//                break;
//            case R.raw.c_sharp:
//                invalidate(mCSharpKey);
//                break;
//            case R.raw.d:
//                invalidate(mDKey);
//                break;
//            case R.raw.d_sharp:
//                invalidate(mDSharpKey);
//                break;
//            case R.raw.e:
//                invalidate(mEKey);
//                break;
//            case R.raw.f:
//                invalidate(mFKey);
//                break;
//            case R.raw.f_sharp:
//                invalidate(mFSharpKey);
//                break;
//            case R.raw.g:
//                invalidate(mGKey);
//                break;
//            case R.raw.g_sharp:
//                invalidate(mGSharpKey);
//                break;
//            case R.raw.a:
//                invalidate(mAKey);
//                break;
//            case R.raw.a_sharp:
//                invalidate(mASharpKey);
//                break;
//            case R.raw.b:
//                invalidate(mBKey);
//                break;
        }
    }

    private void updatePaints() {
        mCKeyPaint = mWhiteKeyPaint;
        mDKeyPaint = mWhiteKeyPaint;
        mEKeyPaint = mWhiteKeyPaint;
        mFKeyPaint = mWhiteKeyPaint;
        mGKeyPaint = mWhiteKeyPaint;
        mAKeyPaint = mWhiteKeyPaint;
        mBKeyPaint = mWhiteKeyPaint;
        mCSharpKeyPaint = mBlackKeyPaint;
        mDSharpKeyPaint = mBlackKeyPaint;
        mFSharpKeyPaint = mBlackKeyPaint;
        mGSharpKeyPaint = mBlackKeyPaint;
        mASharpKeyPaint = mBlackKeyPaint;

        for (Point fingerPoint : mFingerPoints) {
            if (fingerPoint != null) {
                if (mCSharpKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mCSharpKeyPaint = mBlackKeyHitPaint;
                } else if (mDSharpKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mDSharpKeyPaint = mBlackKeyHitPaint;
                } else if (mFSharpKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mFSharpKeyPaint = mBlackKeyHitPaint;
                } else if (mGSharpKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mGSharpKeyPaint = mBlackKeyHitPaint;
                } else if (mASharpKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mASharpKeyPaint = mBlackKeyHitPaint;
                } else if (mCKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mCKeyPaint = mWhiteKeyHitPaint;
                } else if (mDKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mDKeyPaint = mWhiteKeyHitPaint;
                } else if (mEKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mEKeyPaint = mWhiteKeyHitPaint;
                } else if (mFKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mFKeyPaint = mWhiteKeyHitPaint;
                } else if (mGKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mGKeyPaint = mWhiteKeyHitPaint;
                } else if (mAKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mAKeyPaint = mWhiteKeyHitPaint;
                } else if (mBKey.contains(fingerPoint.x, fingerPoint.y)) {
                    mBKeyPaint = mWhiteKeyHitPaint;
                }
            }
        }
    }

    private int getToneForPoint(Point point) {
//        if (mCSharpKey.contains(point.x, point.y))
//            return R.raw.c_sharp;
//        if (mDSharpKey.contains(point.x, point.y))
//            return R.raw.d_sharp;
//        if (mFSharpKey.contains(point.x, point.y))
//            return R.raw.f_sharp;
//        if (mGSharpKey.contains(point.x, point.y))
//            return R.raw.g_sharp;
//        if (mASharpKey.contains(point.x, point.y))
//            return R.raw.a_sharp;
//
//        if (mCKey.contains(point.x, point.y))
//            return R.raw.c;
//        if (mDKey.contains(point.x, point.y))
//            return R.raw.d;
//        if (mEKey.contains(point.x, point.y))
//            return R.raw.e;
//        if (mFKey.contains(point.x, point.y))
//            return R.raw.f;
//        if (mGKey.contains(point.x, point.y))
//            return R.raw.g;
//        if (mAKey.contains(point.x, point.y))
//            return R.raw.a;
//        if (mBKey.contains(point.x, point.y))
//            return R.raw.b;

        return -1;
    }
}
PianoKeyboard.java

 

例二:PaintView

 

public class PaintView extends View {
    public static final int MAX_FINGERS = 5;
    private Path[] mFingerPaths = new Path[MAX_FINGERS];
    private Paint mFingerPaint;
    private ArrayList<Path> mCompletedPaths;
    private RectF mPathBounds = new RectF();

    public PaintView(Context context) {
        super(context);
    }

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

    public PaintView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mCompletedPaths = new ArrayList<Path>();
        mFingerPaint = new Paint();
        mFingerPaint.setAntiAlias(true);
        mFingerPaint.setColor(Color.BLACK);
        mFingerPaint.setStyle(Paint.Style.STROKE);
        mFingerPaint.setStrokeWidth(6);
        mFingerPaint.setStrokeCap(Paint.Cap.BUTT);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for(Path completedPath : mCompletedPaths){
            canvas.drawPath(completedPath, mFingerPaint);
        }
        for(Path fingerPath : mFingerPaths){
            if(fingerPath != null){
                canvas.drawPath(fingerPath, mFingerPaint);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int pointerCount = event.getPointerCount();
        int cappedPointerCount = pointerCount > MAX_FINGERS ? MAX_FINGERS : pointerCount;

        int actionIndex = event.getActionIndex();
        int action = event.getActionMasked();
        int id = event.getPointerId(actionIndex);

        if((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN)
                && id < MAX_FINGERS){
            mFingerPaths[id] = new Path();
            mFingerPaths[id].moveTo(event.getX(actionIndex), event.getY(actionIndex));
        }else if((action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP)
                && id < MAX_FINGERS){
            mFingerPaths[id].setLastPoint(event.getX(actionIndex), event.getY(actionIndex));
            mCompletedPaths.add(mFingerPaths[id]);
            mFingerPaths[id].computeBounds(mPathBounds, true);
            invalidate((int) mPathBounds.left, (int) mPathBounds.top,
                    (int) mPathBounds.right, (int) mPathBounds.bottom);
            mFingerPaths[id] = null;
        }
        for(int i = 0; i < cappedPointerCount; i++){
            if(mFingerPaths[i] != null){
                int index = event.findPointerIndex(i);
                mFingerPaths[i].lineTo(event.getX(index), event.getY(index));
                mFingerPaths[i].computeBounds(mPathBounds, true);
                invalidate((int) mPathBounds.left, (int) mPathBounds.top,
                        (int) mPathBounds.right, (int) mPathBounds.bottom);
            }
        }

        return true;
    }
}
PaintView.java

 

例三:RotationView

 

public class RotateView extends View {
    public static final String LOG_TAG = RotateView.class.getSimpleName();
    private static final double MAX_ANGLE = 1e-1;
    private Paint mPaint;
    private float mRotation;
    private Float mPreviousAngle;

    public RotateView(Context context) {
        super(context);
    }

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

    public RotateView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(10);
        mPaint.setAntiAlias(true);

        mPreviousAngle = null;
    }

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

        int width = getWidth();
        int height = getHeight();
        int radius = (int) (width > height ? height * 0.666f : width * 0.666f) / 2;

        canvas.drawColor(Color.YELLOW);
        canvas.drawCircle(width / 2, height / 2, radius, mPaint);
        canvas.drawRect(0, 50, 200, 100, mPaint);
        canvas.save();
        canvas.rotate(mRotation, width / 2, height / 2);
        canvas.drawLine(width / 2, height * 0.1f, width / 2, height * 0.9f, mPaint);
        canvas.drawRect(0, 50, 200, 100, mPaint);
        canvas.drawRect(500, 550, 700, 600, mPaint);
        canvas.drawCircle(width / 2, height / 2, radius - 200, mPaint);
        canvas.restore();
       // canvas.save();
        canvas.rotate(mRotation / 2, width / 2, height / 2);
        canvas.drawRect(500, 550, 700, 600, mPaint);
        canvas.restore();
        canvas.drawRect(500, 550, 700, 600, mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getPointerCount() == 2){
            float currentAngle = (float) angle(event);
            Log.d(LOG_TAG, "In onTouchEvent currentAngle = " + currentAngle);
            Log.d(LOG_TAG, "In onTouchEvent currentAngle degree = " + Math.toDegrees(currentAngle));
            if(mPreviousAngle != null){
                Log.d(LOG_TAG, "In onTouchEvent mPreviousAngle = " + mPreviousAngle);
                Log.d(LOG_TAG, "In onTouchEvent mPreviousAngle degree = " + Math.toDegrees(mPreviousAngle));
                Log.d(LOG_TAG, "In onTouchEvent mRotation = " + mRotation);
                Log.d(LOG_TAG, "In onTouchEvent mPreviousAngle - currentAngle = " + (mPreviousAngle - currentAngle));
                Log.d(LOG_TAG, "In onTouchEvent MAX_ANGLE = " + MAX_ANGLE);
                Log.d(LOG_TAG, "In onTouchEvent clamp = " + clamp(mPreviousAngle - currentAngle, -MAX_ANGLE, MAX_ANGLE));
                mRotation -= Math.toDegrees(clamp(mPreviousAngle - currentAngle,
                        -MAX_ANGLE, MAX_ANGLE)); // (float)
//                mRotation -= Math.toDegrees(mPreviousAngle - currentAngle);
                invalidate();
            }
            mPreviousAngle = currentAngle;
        }else{
            mPreviousAngle = null;
        }
        return true;
    }

    private static double angle(MotionEvent event){
        double deltaX = (event.getX(0) - event.getX(1));
        double deltaY = (event.getY(0) - event.getY(1));
        Log.d(LOG_TAG, "In angle x = " + deltaX + ", y = " + deltaY);
        return Math.atan2(deltaY, deltaX);
    }

    private static double clamp(double value, double min, double max){
        if(value < min){
            Log.d(LOG_TAG, "In clamp min ---------------------------------------------------------------------------------------------------------------------------------------");
            return min;
        }
        if(value > max){
            Log.d(LOG_TAG, "In clamp max ---------------------------------------------------------------------------------------------------------------------------------------");
            return max;
        }
        return value;
    }
}
RotateView.java

 

@、OpenGL ES

https://developer.android.com/guide/topics/graphics/opengl.html

1、  开源3D引擎Rajawali

https://github.com/Rajawali/Rajawali

2、  商用引擎Unity3D

http://unity3d.com/cn/

 

转载于:https://www.cnblogs.com/yarightok/p/5630380.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
First Edition Build Android Apps That Are Stunningly Attractive, Functional, and Intuitive In today’s crowded Android marketplace, it’s more important than ever to differentiate your apps. Great design is the best way to do that. Now, leading Android app design expert Ian G. Clifton shows you how to make your apps come alive and how to deliver apps that users will want, love, and buy! Reflecting the Android 4.2 SDK, this book serves both as a tutorial for the entire design and implementation process and as a handy reference you’ll rely on for every Android development project. Clifton shows how to create effective designs, organize them into Android components, and move gracefully from idea, to wireframe, to comp, to finished app. You’ll learn how to bring your own voice, personality, and style to your app designs; how to leverage advanced drawing techniques such as PorterDuff compositing; how to test designs on diverse Android devices; and much more. Android User Interface Design details each step of the design and development process and contains extensive downloadable sample code, including complete finished apps. Learn how Android has evolved to support outstanding app design Integrate app design with development, from idea through deployment Understand views, the building blocks of Android user interfaces Make the most of wireframes and prototypes Build efficient layouts and integrate smooth animations Make apps more useful by automatically updating ListViews Combine views into custom components Use image compositing and other advanced techniques Work with the canvas and advanced drawing Leverage Google Play and Amazon Appstore assets One step at a time, this guide helps you bridge the gap between Android developers and designers so you can work with colleagues to create world-class app designs...or do it yourself!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值