这是下一阶段: 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));
答案 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>
我没有理解你所有的缩放代码,所以我摆脱它,并尽可能简单,你可以从这里工作,并在需要时添加一些调整大小的代码。