Android显示尺寸和可绘制尺寸缩放

时间:2014-08-14 19:02:11

标签: java android bitmap screen scale

这是下一阶段: Scale and align in custom android AnalogClock hands ...这个。

很快我意识到Drawable无法缩放,如果它适合尺寸框,那么它将被显示。否则不是。所以现在我需要一个Bitmap来缩放它,然后转换回Drawable并绘制它。它看起来并不优雅,但根据我的发现,我没有看到任何其他方式。

(想法是用一些特殊技巧创建自己的闹钟应用程序。首先,我需要时钟指针。为了创建自定义时钟,我使用了自己的类,它扩展了View。时针和分针是PNG图像。它们应该位于屏幕的中心,但它们甚至都不可见。模拟器根本不显示时钟,并且没有任何ecxeptions(显示基本层)

我再次需要屏幕尺寸来设置小时和分针的大小。代码现在看起来像:

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


}
        }

对于那个大代码再次抱歉。现在Eclipse中的图形布局管理器声明了这个错误:

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) 

结果,sizeXHour等为0;我不确定时钟指针是否会被绘制,但我需要获取屏幕尺寸。有什么可能的想法吗?

P.S。首先是例外:                     mHourHand = new BitmapDrawable(getResources(),Bitmap.createScaledBitmap(HourHnd,sizeXHour,sizeYHour,true));

1 个答案:

答案 0 :(得分:0)

因此出现此异常是因为eclipse无法完全解析某些属性(如样式等)......当错误发生时,可视编辑器会对此进行解释。一些解决方法是使用View.isInEditMode()

至于获取屏幕大小,您使用的代码(Display)是正确的,应该可以使用。如果时钟没有出现,这意味着其他错误,而不是测量。

我认为一般来说,测量屏幕的大小是一个坏主意,因为你假设你的视图与整个屏幕相匹配。但是没有什么可以阻止你的视图被放置在其他容器中,因此具有更小的实际尺寸。

我建议您使用View.getWith()View.getHeight()来使用视图的边界。 注意:由于视图是在构造之后测量的,因此在构造函数中使用这些方法返回0.您必须等到视图布局以检索其维度:用于此View.getViewTreeObserver().setOnGlobalLayoutListener


另外:

  • 在您的视图布局后,系统会自动调用onDraw,因此无需在invalidate结束时调用onMeasure

  • 更改的布尔值没有效果(似乎)。如果想要使用它只允许在大小改变时绘制时钟,那么这是一个坏主意:这将导致视图在其无效时被“擦除”,直到其边界发生变化。


以下是我的工作方式:

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

然后,这是一个在ActivityInstrumentationTestCase2内运行的测试:

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

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

布局文件:

<?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>

我没有理解你所有的缩放代码,所以我摆脱它,并尽可能简单,你可以从这里工作,并在需要时添加一些调整大小的代码。