Scale and align in custom android AnalogClock hands

ぃ、小莉子 提交于 2019-12-24 10:55:46

问题


I searched the topic for several days, but finally confused. The idea is to create own alarm app with some special tricks. Firstly, I need clock hands. For creating custom clock, I used own class, which extends View. Hour and minute hands are PNG images. They should be located in the center of the screen, but they dont. Actually, I can't even see them. And that is the question.

Here is the Clock class

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
//import android.os.Handler;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;



@SuppressLint("NewApi")
public class Clock extends View {

public Clock(Context context) {
    super(context);
    // TODO Auto-generated constructor stub
}


private Drawable mHourHand;
private Drawable mMinuteHand;

private boolean mAttached;

static private float mMinutes;
static private float mHour;
private boolean mChanged;

Context mContext;
private boolean mSeconds;

// Gettes & setters. These clock must present alarm time which user sets in the next view

protected float getmMinutes() {
return mMinutes;
}

protected static void setmMinutes(float mMinutes) {
Clock.mMinutes = mMinutes;
}

protected float getmHour() {
return mHour;
}

protected static void setmHour(float mHour) {
Clock.mHour = mHour;
}


private Point size;

// ctors

public Clock(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}


public Clock(Context context, AttributeSet attrs,
        int defStyle) {
        super(context, attrs, defStyle);
        Resources r = context.getResources();
        TypedArray a =
        context.obtainStyledAttributes(attrs, R.styleable.AnalogClock, defStyle, 0);
        mContext=context;

        mHourHand = r.getDrawable(R.drawable.hours);

        mMinuteHand = r.getDrawable(R.drawable.minuts);
}



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

    if (!mAttached) {
        mAttached = true;
        IntentFilter filter = new IntentFilter();

        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

     // getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
    }

 }

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {


    int desiredWidth = 150;  // and yes, 150 what? px, inches, dpi-s? I draw it just randomly
    int desiredHeight = 150;

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;
    int height;

    //Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
        //Must be this size
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        width = Math.min(desiredWidth, widthSize);
    } else {
        //Be whatever you want
        width = desiredWidth;
    }

    //Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
        //Must be this size
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        height = Math.min(desiredHeight, heightSize);
    } else {
        //Be whatever you want
        height = desiredHeight;
    }

    setMeasuredDimension(width, height);

}


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




@SuppressWarnings({ "deprecation" })
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    boolean changed = mChanged;
    if (changed) {
        mChanged = false;
    }

    boolean seconds = mSeconds;
    if (seconds ) {
        mSeconds = false;
    }

    int w = 100;  //These are too made randomly
    int h = 100;  

    WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
   // if (android.os.Build.VERSION.SDK_INT <= 13)
   // {
        w = display.getWidth();  // deprecated
        h = display.getHeight();  // deprecated

    //}
   // else if (android.os.Build.VERSION.SDK_INT > 13)
    //{
        //size = null;
        //display.getSize(size);
        //w = size.x;
        //h = size.y;
    //}   ... I cant figure out, why, but size returns null. So I'll use deprecated ones just for     
     now.

    // **Here are my measures. I suggest that if height of an hour hand should be about 1/4 of   
       screen width, then following the proportion - width of that hand should be old width*new 
       height/old height, or smthng**   

    int sizeXHour = w/3;
        int sizeYHour = mHourHand.getIntrinsicHeight()*sizeXHour/mHourHand.getIntrinsicWidth();

        int xHour = sizeXHour/2;
        int yHour = sizeYHour/2;

        canvas.rotate(mHour / 12.0f * 360.0f, xHour, yHour);
        final Drawable hourHand = mHourHand;
        if (changed) {
        hourHand.setBounds((w / 2) - xHour, (h / 2) - yHour, sizeXHour, sizeYHour);
        }
        hourHand.draw(canvas);
        canvas.restore();

        int sizeYMinute = h/4;
        int sizeXMinute = mMinuteHand.getIntrinsicWidth()*sizeYMinute/mMinuteHand.getIntrinsicHeight();

        int xMinute = sizeXMinute/2;
        int yMinute = sizeYMinute/2;

        canvas.save();
        canvas.rotate(mMinutes / 60.0f * 360.0f, xMinute, yMinute);
        final Drawable minuteHand = mMinuteHand;
        if (changed) {
            minuteHand.setBounds((w / 2 - xMinute), (h / 2 - yMinute), sizeXMinute, sizeYMinute);
        }
        minuteHand.draw(canvas);
        canvas.restore();
        canvas.save();

}

I feel that something is very wrong, but can not figure what. The XML is:

<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="ee.st.running.dreamyclock.MainActivity"
android:background = "@drawable/clockk" >

<View
    android:id="@+id/view1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<st.running.dreamyclock.Clock
    android:id="@+id/clock"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical = "true"
    android:layout_marginTop="124dp" />
</RelativeLayout>

the attrs are:

  <?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AnalogClock">
    <attr name="hand_hour" format="reference"/>
    <attr name="hand_minute" format="reference"/>
</declare-styleable>
</resources>

Any ideas where it came from? Thank you


回答1:


Was this ever working? Try to add this to Clock constructor: setWillNotDraw(false);

"If this view doesn't do any drawing on its own, set this flag to allow further optimizations. By default, this flag is not set on View, but could be set on some View subclasses such as ViewGroup. Typically, if you override onDraw(android.graphics.Canvas) you should clear this flag."

Edit: If you want to re draw your view, you should call invalidate() method

"Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. "

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mChanged = true;
    this.invalidate();
}

You don't even have to call invalidate(); if you just want to for example rotate the view:

 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mChanged = true;
        setRotation(20);
    }

Edit 2:

When you are setting bounds for a Drawable, the "right" value should be higher than "left" for example: minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2) + 4, y + (h / 4)); or minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2) + minuteHand.getIntrinsicWidth(), y + (h / 4));



来源:https://stackoverflow.com/questions/25296081/scale-and-align-in-custom-android-analogclock-hands

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!