简体   繁体   中英

Android display Dimensions and Drawable size scaling

It is the next stage of: Scale and align in custom android AnalogClock hands ...this one.

Soon I realized that a Drawable can not be scaled, if it fits the size box, then it will be displayed. Otherwise not. So now I need a Bitmap to scale it and then convert back to Drawable and draw it. It doesn't seem elegant, but according to what I found, I don't see any other way.

(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 are not even visible. Emulator do not display the clock at all, and is not throwing any ecxeptions (basic layer is displayed) )

Again I need screen dimensions to set up size of Hour and Minute hands. The code now looks as:

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
import android.widget.ImageView;



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

private Drawable mHourHand;
private Drawable mMinuteHand;

private int sizeXHour;
private int sizeXMinute;
private int sizeYHour;
private int sizeYMinute;
private int xHour;
private int xMinute;
private int yHour;
private int yMinute;
private int w;
private int h;

private Drawable Hh;
private Bitmap HourHnd;
private Drawable Mh;
private Bitmap MinuteHnd;

private boolean mAttached;

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

Context mContext;

// Getters&setters

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;
}


//Ctors

@SuppressWarnings("deprecation")
public Clock(Context context) {
super(context);
Resources r = context.getResources();
mContext=context;

Hh = r.getDrawable(R.drawable.hours);
HourHnd = ((BitmapDrawable)Hh).getBitmap();

Mh = r.getDrawable(R.drawable.minuts);
MinuteHnd = ((BitmapDrawable)Mh).getBitmap();   

// Here I try to take screen dimensions    

WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2)
{

    DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();

    h = metrics.heightPixels;
    w = metrics.widthPixels;

}
else
{
    Display d = wm.getDefaultDisplay();
    h = d.getWidth();
    w = d.getHeight();
}


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

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

    mHourHand = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(HourHnd, sizeXHour, sizeYHour, true));   // Here it says that sizeXHour etc. is 0;

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

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

mMinuteHand = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(MinuteHnd, sizeXMinute, sizeYMinute, true));


setWillNotDraw(false);
}



@SuppressWarnings("deprecation")
public Clock(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    //setWillNotDraw(false);
    Resources r = context.getResources();
     mContext=context;


     Hh = r.getDrawable(R.drawable.hours);
     HourHnd = ((BitmapDrawable)Hh).getBitmap();

     Mh = r.getDrawable(R.drawable.minuts);
     MinuteHnd = ((BitmapDrawable)Mh).getBitmap();   


     WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);

     //if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2)
     //{

        //DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();

        //h = metrics.heightPixels;
        //w = metrics.widthPixels;

     //}
     //else
     //{
         Display d = wm.getDefaultDisplay();
         h = d.getWidth();
         w = d.getHeight();
     //}


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

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

        mHourHand = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(HourHnd, sizeXHour, sizeYHour, true));

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

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

        mMinuteHand = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(MinuteHnd, sizeXMinute, sizeYMinute, true));


     setWillNotDraw(false);

}


@SuppressWarnings("deprecation")
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);
         Hh = r.getDrawable(R.drawable.hours);
         HourHnd = ((BitmapDrawable)Hh).getBitmap();


         //mMinuteHand = r.getDrawable(R.drawable.minuts);
         Mh = r.getDrawable(R.drawable.minuts);
         MinuteHnd = ((BitmapDrawable)Mh).getBitmap();   



         WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         //if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2)
         //{

         // DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();

            //h = metrics.heightPixels;
         // w = metrics.widthPixels;
             //h = size.x;
             //w = size.y;
         //}
         //else
        // {
             Display d = wm.getDefaultDisplay();
             h = d.getWidth();
             w = d.getHeight();
         //}


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

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

            mHourHand = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(HourHnd, sizeXHour, sizeYHour, true));

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

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

            mMinuteHand = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(MinuteHnd, sizeXMinute, sizeYMinute, true));


         setWillNotDraw(false);
}



@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);

    }

}



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


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

    int width = widthSize; 
    int height = heightSize;


    setMeasuredDimension(width, height);


}


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




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

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


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



        canvas.rotate(mMinutes / 60.0f * 360.0f, xMinute, yMinute);

        if (changed) {
        mMinuteHand.setBounds((w / 2 - xMinute), (h / 2 - yMinute), sizeXMinute, sizeYMinute);
        /
        }
        mMinuteHand.draw(canvas);
        //canvas.restore();
        canvas.save();


}
        }

Sorry for that large code again. Now the graphic layout manager in Eclipse states this error:

java.lang.IllegalArgumentException: width and height must be > 0
at android.graphics.Bitmap.createBitmap(Bitmap.java:808)
at android.graphics.Bitmap.createBitmap(Bitmap.java:787)
at android.graphics.Bitmap.createBitmap(Bitmap.java:719)
at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:595)
at ee.st.running.dreamyclock.Clock.<init>(Clock.java:268)
at ee.st.running.dreamyclock.Clock.<init>(Clock.java:161)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(    at       sun.reflect.NativeConstructorAccessorImpl.newInstance(    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(    at java.lang.reflect.Constructor.newInstance(    at com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.instantiateClass(ProjectCallback.java:438)
at com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.loadView(ProjectCallback.java:190)
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:207)
at android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:132)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:802)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:64)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:778)
at android.view.LayoutInflater.inflate(LayoutInflater.java:500)
at android.view.LayoutInflater.inflate(LayoutInflater.java:381) 

In result, sizeXHour etc is 0; I am not sure that clock hands will be drawn at all, but I need to acquire screen dimensions nevertheless. Are there any possible ideas?

PS Exception is firstly here: mHourHand = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(HourHnd, sizeXHour, sizeYHour, true));

So this exception appears because eclipse cannot fully resolve some attributes such as styles, etc ... It is explained in the visual editor when the error occurs. Some workaround is to use View.isInEditMode() .

As for getting the size of the screen, the code you use (with Display ) is correct and should work. If the clock doesn't appear, this means something else is wrong, not measurements.

I consider measuring the size of the screen a bad idea in general, because you make the assumption that your view matches the whole screen. But nothing prevents your view to be placed inside some other container and therefore have a real size that's smaller.

I suggest then, that you use the bounds of your view using View.getWith() and View.getHeight() . Note : since a view is measured after its construction, using these methods in the constructors returns 0. You have to wait until the view has been laid out to retrieve its dimension : use for this View.getViewTreeObserver().setOnGlobalLayoutListener .


Also :

  • After your view is laid out, onDraw will automatically be called, so there's no need to call invalidate at the end of onMeasure .

  • The boolean changed has no effect (it seems). If the idea was to use it to only allow drawing the clock if size changed, it is a bad idea : this will result in your view being 'erased' when it is invalidated until its bounds change.


Here is how i made it work :

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

    private Drawable mHourHand;
    private Drawable mMinuteHand;

    private Drawable Hh;
    private Drawable Mh;

    private boolean mAttached;

    static private float mMinutes = 43;
    static private float mHour = 5;

    Context mContext;

    // Getters&setters

    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;
    }

    // Ctors

    {
        initView();
    }

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

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

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

    private void initView() {
        Resources r = getContext().getResources();
        mContext            = getContext();
        Hh                  = r.getDrawable(R.drawable.hours);
        Mh                  = 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);

        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int w               = r - l;
        int h               = b - t;

        mHourHand           = Hh;
        mMinuteHand         = Mh;

        // They will always be drawn centered inside the view
        mHourHand.setBounds(
                (w - mHourHand.getIntrinsicWidth()) / 2,
                (h - mHourHand.getIntrinsicHeight()) / 2,
                (w + mHourHand.getIntrinsicWidth()) / 2,
                (h + mHourHand.getIntrinsicHeight()) / 2
        );
        mMinuteHand.setBounds(
                (w - mMinuteHand.getIntrinsicWidth()) / 2,
                (h - mMinuteHand.getIntrinsicHeight()) / 2,
                (w + mMinuteHand.getIntrinsicWidth()) / 2,
                (h + mMinuteHand.getIntrinsicHeight()) / 2
        );
    }

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

        // Rotate the center of the view (here the canvas is clipped and translated so that its clipped bounds are these of this view).
        canvas.save();
        canvas.rotate(mHour * 360/12, getWidth()/2, getHeight()/2);
        mHourHand.draw(canvas);
        canvas.restore();

        canvas.save();
        canvas.rotate(mMinutes * 360/12, getWidth()/2, getHeight()/2);
        mMinuteHand.draw(canvas);
        canvas.restore();
    }
}

Then, here's a test, running inside an ActivityInstrumentationTestCase2 :

public void test() throws InterruptedException {
    getActivity().runOnUiThread(new Runnable() {

        @Override
        public void run() {
            getActivity().setContentView(R.layout.clock_layout);
        }
    });
    Thread.sleep(3000);
}

And the layout file :

<?xml version="1.0" encoding="utf-8"?>
<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"
    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" />
    <!-- A modified layout params to match_parent, wrap_content results in the view having size (0,0) -->
    <com.example.clock.Clock
         android:id="@+id/clock"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignTop="@+id/view1"
         android:layout_centerHorizontal="true" />
</RelativeLayout>

I didn't understood all your scaling code, so i just got rid of it, and made as simple as possible, you can then work from this and add some resizing code if needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM