0

AnalogClockアプリウィジェットに配置されたAndroidにカスタム時間を設定するには?

別の方法として、デフォルトの AnalogClock をオーバーライドして、コードで時間を設定することを考えていました。言い換えれば、デフォルト ビューまたは AnalogClock から拡張するカスタム クロックを作成します。次に、カスタム時計をウィジェット レイアウト UI に配置します。

これは可能ですか?残念ながら、RemoteViews は独自のカスタム コンポーネントを持つように制限されています。

更新: これは、最初に vArDo によって提供された解決策に従っているエラー ログです。

ここに画像の説明を入力

4

2 に答える 2

6

イントロ

アプリウィジェットにカスタムビューを含めることはできません。ドキュメントによると、レイアウトで使用できるのは、事前定義されたビューの小さなサブセットのみです。したがって、あなたが言及しAnalogClockたように、(私が他の回答で示したように)独自のビューを作成してアプリウィジェットに含めることはできません。

AnalogClockただし、アプリウィジェットでカスタムを作成するために使用できる他の手段があります。このような実装では、時間を直接設定するAnalogClockことは問題にはなりません。

短い答え

AnalogClockこれを実現する方法の1つは、カスタムを描画することですImageView(これは、アプリウィジェットで許可されているビューの1つです)。繰り返しは、60秒ごとに描画するためにPendingIntent使用しています(経由)。ServiceImageViewRemoteViews

電池使用量に関する注意

60秒ごとにアプリウィジェットService更新するアクションを呼び出すことは、バッテリーでそれほど軽量ではないことに注意してください。Jelly Beanの実装例では、夜間に2%のバッテリーを使用しました(スクリーンオフ、平均ネットワーク強度)。ただし、15秒ごとに画面を更新しているので、アナログ時計の場合は必要ありません。したがって、大まかな見積もりでは、1分あたり1回の更新を行うアプリウィジェットは、夜間にバッテリージュースの約0.5%を消費します。これは、ユーザーにとって重要な場合があります。RemoteViews

ソリューションの詳細

描画サービスの変更

まず最初に、ビューに表示されるものを作成する必要がありServiceますImageView。描画コードは、AnalogClock実装からほとんど取得され、必要に応じて再配置されます。まず第一に、時間が変更されたかどうかのチェックはありません-これはService、私たちも決定したときにのみ呼び出されるためです(たとえば、60秒ごと)。2番目の変更はDrawable、メソッド内のリソースからsを作成することです。

もう1つの変更は、コードが以下をBitmap使用してアナログクロックをローカルに描画することCanvasです。

    // Creating Bitmap and Canvas to which analog clock will be drawn.
    Bitmap appWidgetBitmap = Bitmap.createBitmap(availableWidth, availableHeight, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(appWidgetBitmap);

ImageView更新は経由で引き込まれRemoteViewsます。変更は集約され、ホーム画面のアプリウィジェットにプッシュされます。詳細については、ドキュメントを参照してください)。私たちの場合、変更は1つだけで、特別な方法で行われます。

    remoteViews.setImageViewBitmap(R.id.imageView1, appWidgetBitmap);

コードが機能するためには、アナログ時計を描画するために利用可能なスペースを宣言する必要があることに注意してください。で宣言されている内容に基づいてwidget_provider.xml、の次元を決定できますImageView。描画する前に、dp単位を単位に変換する必要があることに注意してください。px

    // You'll have to determine tour own dimensions of space to which analog clock is drawn.
    final int APP_WIDGET_WIDTH_DP = 210; // Taken from widget_provider.xml: android:minWidth="210dp"
    final int APP_WIDGET_HEIGHT_DP = 210; // Taken from widget_provider.xml: android:minHeight="210dp"

    final int availableWidth = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_WIDGET_WIDTH_DP, r.getDisplayMetrics()) + 0.5f);
    final int availableHeight = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_WIDGET_HEIGHT_DP, r.getDisplayMetrics()) + 0.5f);

ここServiceにサブクラスの完全なコードを貼り付けました。

また、上部には、表示される時間を設定するためのコードがあります。必要に応じて変更してください。

    mCalendar = new Time();

    // Line below sets time that will be displayed - modify if needed.
    mCalendar.setToNow();

    int hour = mCalendar.hour;
    int minute = mCalendar.minute;
    int second = mCalendar.second;

    mMinutes = minute + second / 60.0f;
    mHour = hour + mMinutes / 60.0f;

また、ファイルでこれServiceとアプリウィジェットを宣言することを忘れないでください。例:AndroidManifest.xml

    <receiver 
        android:name="TimeSettableAnalogClockAppWidget"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">

        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
            <action android:name="android.appwidget.action.APPWIDGET_DISABLED"/>
        </intent-filter>

        <meta-data 
            android:name="android.appwidget.provider"
            android:resource="@xml/widget_provider"/>            

    </receiver>
    <service android:name="TimeSettableAnalogClockService"/>

サービスを使用してアプリウィジェットを更新することに関する最も重要な情報は、このブログ投稿で簡単に説明されています。

図面の呼び出し

更新のスケジュールは、サブクラスonUpdate()のメソッドで実行されます。これは、ファイルで宣言されています。アプリウィジェットがホーム画面から削除されたときにも削除する必要があります。これは、オーバーライドされたメソッドで実行されます。各メソッドの1つを呼び出す両方のアクションを宣言することを忘れないでください:および。上からの抜粋を参照してください。AppWidgetProviderAndroidManifest.xmlPendingIntentonDisabled()APPWIDGET_UPDATEAPPWIDGET_DISABLEDAndroidManifest.xml

package com.example.anlogclocksettimeexample;

import java.util.Calendar;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;

public class TimeSettableAnalogClockAppWidget extends AppWidgetProvider {

    private PendingIntent service = null;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
//      super.onUpdate(context, appWidgetManager, appWidgetIds);
        final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

        final Calendar time = Calendar.getInstance();
        time.set(Calendar.SECOND, 0);
        time.set(Calendar.MINUTE, 0);
        time.set(Calendar.HOUR, 0);

        final Intent intent = new Intent(context, TimeSettableAnalogClockService.class);

        if (service == null) {          
            service = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        }

        alarmManager.setRepeating(AlarmManager.RTC, time.getTime().getTime(), 1000 * 60 /* ms */, service);
    }

    @Override
    public void onDisabled(Context context) {
        final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(service);
    }
}

メソッドの最後の行はonUpdate()、最初の更新がすぐに発生するように、60秒ごとに発生する繰り返しイベントをスケジュールします。

AnalogClock一度だけ描く

上記のコードを使用して、サービスを1回だけ呼び出す最も簡単な方法は、1つのイベントを繰り返すのではなく、時間内に1つのイベントをスケジュールすることです。alarmManager.setRepeating()次の行で呼び出しを置き換えるだけです。

alarmManager.set(AlarmManager.RTC, time.getTime().getTime(), service);

結果

以下は、時刻が1回だけ設定されたカスタムアプリウィジェットのスクリーンショットAnalogClockです(アナログ時計の時刻とステータスバーに表示される時刻の違いに注意してください)。

ここに画像の説明を入力してください

于 2012-08-13T21:09:43.087 に答える
2

の現在時刻以外に時刻を設定する方法はありませんAnalogClock。このウィジェットは非常にカプセル化されています。外観は簡単に変更できます(XMLではR.styleableの属性を使用しますが、動作を簡単に変更する方法はありません。

AnalogClock.javaのコピー(たとえば、別のパッケージに入れることをお勧めしますcom.example.widget)とドローアブルのローカルコピーclock_dialclock_hand_hourclock_hand_minute(注:最良の結果を得るには、作成する必要があります)を作成することをお勧めします。すべての密度のローカルコピー:ldpimdpihdpixhdpi)。これらを適切に配置し、コードに小さな変更を加えることで、必要な動作を実現できます。

XMLレイアウトから設定可能な属性を持つカスタムドローアブルの使用

XMLレイアウトファイルからドローアブルを設定できるようにするにはdial、これらの属性をカスタムウィジェットのスタイリング可能として宣言する必要があります。hand_hourhand_minute

注:次の例では、パッケージを作成TimeSettableAnalogClockして配置しました。com.example.widget

res/values/attrs.xml次の内容でファイルを作成します。

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

format=reference値は、他の既存の要素(たとえば、drawables(@drawable/some_custom_dial))への参照のみである可能性があることを意味します。

これで、カスタム属性をXMLレイアウトファイルで使用できるようになりました。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/lib/com.example.widget"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.widget.TimeSettableAnalogClock
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"        
        custom:dial="@drawable/some_custom_clock_dial"
        custom:hand_hour="@drawable/some_custom_clock_hand_hour"
        custom:hand_minute="@drawable/some_custom_clock_hand_minute"/>

</RelativeLayout>

TimeSettableAnalogClock最後に、オブジェクトの作成時にこれらのカスタム属性を処理する必要があります。

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

    mDial = a.getDrawable(R.styleable.TimeSettableAnalogClock_dial);
    if (mDial == null) {
        mDial = r.getDrawable(R.drawable.clock_dial);
    }

    mHourHand = a.getDrawable(R.styleable.TimeSettableAnalogClock_hand_hour);
    if (mHourHand == null) {
        mHourHand = r.getDrawable(R.drawable.clock_hand_hour);
    }

    mMinuteHand = a.getDrawable(R.styleable.TimeSettableAnalogClock_hand_minute);
    if (mMinuteHand == null) {
        mMinuteHand = r.getDrawable(R.drawable.clock_hand_minute);
    }

    mCalendar = new Time();

    mDialWidth = mDial.getIntrinsicWidth();
    mDialHeight = mDial.getIntrinsicHeight();
}

注意してください、私はドローアブルまたはスタイリングcom.android.internal.可能なものへの参照からほとんど削除しただけです。残りはAnalogClockコンストラクターと同じです。

XMLレイアウトから設定可能な属性なしでカスタムドローアブルを使用する

XMLを介して属性を設定可能にする必要がない場合(たとえば、すべてのアナログ時計はすべての場所で同じように見える)、簡単に解決できます。(冒頭で述べたように)ドローアブルのローカルコピーのみが必要であり、attrs.xmlファイルは必要ありません。コンストラクターは次のようになります。

public TimeSettableAnalogClock(Context context, AttributeSet attrs,
                   int defStyle) {
    super(context, attrs, defStyle);
    Resources r = getContext().getResources();

    mDial = r.getDrawable(R.drawable.clock_dial);
    mHourHand = r.getDrawable(R.drawable.clock_hand_hour);
    mMinuteHand = r.getDrawable(R.drawable.clock_hand_minute);

    mCalendar = new Time();

    mDialWidth = mDial.getIntrinsicWidth();
    mDialHeight = mDial.getIntrinsicHeight();
}

ご覧のとおり、はるかに短いです。XMLレイアウトでの使用法は同じですが、一部は必要ありませxmlns:customん。もちろん、カスタム属性を設定することはできなくなります。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.widget.TimeSettableAnalogClock
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

これについてさらにサポートが必要な場合は、提案されたソリューションのコードを追加して、回答をさらに更新できます。

于 2012-08-01T13:56:46.143 に答える