3

Android ライブラリでは、FragmentActivityActivityを拡張します。元のActivityのいくつかのメソッドを追加し、いくつかのメソッドをオーバーライドしたいと思います。

import android.app.Activity

public class Activiti extends Activity {
    public void myNewMethod() { ... }
}

元の階層のため、FragmentActivityActivityを拡張myNewMethod()し、私のライブラリにも存在する必要がありますFragmentActiviti

import android.support.v4.app.FragmentActivity;

public abstract class FragmentActiviti extends FragmentActivity {
    public void myNewMethod() { ... }
}

しかし、これはコードの重複につながります。これは望ましくありません。この重複を避ける方法はありますか?


編集:使用シナリオ

Activiti.java

public abstract class Activiti extends Activity {
    private int current_orientation = Configuration.ORIENTATION_UNDEFINED;  // ORIENTATION_UNDEFINED = 0

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        current_orientation = this.getResources().getConfiguration().orientation;
    }
    protected boolean isDevicePortrait() { return current_orientation == Configuration.ORIENTATION_PORTRAIT; }
}

FragmentActiviti.java

public abstract class FragmentActiviti extends FragmentActivity {
    /* This onCreate() can be omitted. Just putting here explicitly. */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    protected void someUtilsForFragments() { /* not used yet */ }
}

E_fragtest_06.java

public class E_fragtest_06 extends FragmentActiviti {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.out.printf(isDevicePortrait());      // this NOT WORK for now
    }
}

編集2:Utilクラスを使ってみてください

この問題を解決するには、 Decorator クラスを使用するのが最も良い方法だと思います(コードの重複はありません)。ただし、Decorator パターンを Android アクティビティ シナリオに適用するのは少し難しい (または不可能) です。

@hazzik のアプローチを実装してみましたが、まだいくつかの問題が発生しています。

ActivityUtil.java

public abstract class ActivityUtil {
    private int current_orientation = Configuration.ORIENTATION_UNDEFINED;  // ORIENTATION_UNDEFINED = 0

    public void onCreate(Activity activity, Bundle savedInstanceState) {
        activity.onCreate(savedInstanceState);
        current_orientation = activity.getResources().getConfiguration().orientation;
    }
    public boolean isDevicePortrait() { return current_orientation == Configuration.ORIENTATION_PORTRAIT; }
}

Activiti.java

public class Activiti extends Activity {
    private ActivityUtil activityUtil;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        activityUtil.onCreate(this, savedInstanceState);
    }
    protected boolean isDevicePortrait() { return activityUtil.isDevicePortrait(); }
}

FragmentActiviti.java

public abstract class FragmentActiviti extends FragmentActivity {
    private ActivityUtil activityUtil;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        activityUtil.onCreate(this, savedInstanceState);
    }
    protected boolean isDevicePortrait() { return activityUtil.isDevicePortrait(); }
}

ではActivityUtil.onCreate()、次のactivity.onCreate(savedInstanceState);コンパイル エラーが発生しています。

タイプ Activity のメソッド onCreate(Bundle) は表示されません。

私がに変更Activityした場合Activiti

public abstract class ActivityUtil {
    public void onCreate(Activiti activity, Bundle savedInstanceState) { ... }
    ...
}

FragmentActiviti.onCreate()の で別のコンパイル エラーが発生しますactivityUtil.onCreate()

タイプ ActivityUtil のメソッド onCreate(Activiti, Bundle) は、引数 (FragmentActiviti, Bundle) には適用されません。

これらのエラーが発生する理由を理解しています。しかし、私はそれらを回避する方法を知りません。


この質問に貢献してくれたすべての人、特に @flup がDecorator パターンについて私を導いてくれたことに感謝します。

私の強化された AndroidActivityFragmentActivityクラス。

Android アプリケーションも開発している場合は、私のコードが何らかの形で役立つことを願っています :-)

ActivityCore.java

package xxx.android;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;

public final class ActivityCore {
    public interface ActivityCallbackInterface {
        public void onCreateCallback(Bundle savedInstanceState);
        public void onBeforeSaveInstanceState(Bundle outState);
        public void onSaveInstanceStateCallback(Bundle outState);
    }

    private final Activity activity;
    /**
     * This current_orientation variable should be once set, never changed during the object life-cycle.
     * But Activity.getResources() is not yet ready upon the object constructs.
     * That's why THIS CLASS is wholly responsible to maintain THIS VARIABLE UNCHANGED.
     */
    private int current_orientation = Configuration.ORIENTATION_UNDEFINED;  // ORIENTATION_UNDEFINED = 0

    public ActivityCore(Activity activity) { this.activity = activity; }

    public void onCreate(Bundle savedInstanceState) {
        ((ActivityCallbackInterface) activity).onCreateCallback(savedInstanceState);
        current_orientation = activity.getResources().getConfiguration().orientation;
    }

    public void onSaveInstanceState(Bundle outState) {
        /**
         * THIS is the best ever place i have found, to unload unwanted Fragments,
         * thus prevent re-creating of un-needed Fragments in the next state of Activity.
         *   (state e.g. Portrait-to-Landscape, or Landscape-to-Portrait)
         * 
         * The KEY is to do it BEFORE super.onSaveInstanceState()
         *   (my guess for this reason is, in super.onSaveInstanceState(),
         *    it saves the layout hierarchy, thus saved the Fragments into the Bundle also.
         *    Thus restored.
         *    Note that Fragments NOT IN LAYOUT, having ONLY TAGS, are also restored.)
         */
        ((ActivityCallbackInterface) activity).onBeforeSaveInstanceState(outState);
        ((ActivityCallbackInterface) activity).onSaveInstanceStateCallback(outState);
    }

    public int getCurrentOrientation() { return current_orientation; }

    public boolean isDevicePortrait() { return current_orientation == Configuration.ORIENTATION_PORTRAIT; }
    public boolean isDeviceLandscape() { return current_orientation == Configuration.ORIENTATION_LANDSCAPE; }
    public boolean isNewDevicePortrait() { return activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; }
    public boolean isNewDeviceLandscape() { return activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; }
    public boolean isPortrait2Landscape() { return isDevicePortrait() && isNewDeviceLandscape(); }
    public boolean isLandscape2Portrait() { return isDeviceLandscape() && isNewDevicePortrait(); }

    public String describeCurrentOrientation() { return describeOrientation(current_orientation); }
    public String getCurrentOrientationTag() { return getOrientationTag(current_orientation); }
    public String describeNewOrientation() { return describeOrientation(activity.getResources().getConfiguration().orientation); }
    public String getNewOrientationTag() { return getOrientationTag(activity.getResources().getConfiguration().orientation); }
    private String describeOrientation(final int orientation) {
        switch (orientation) {
            case Configuration.ORIENTATION_UNDEFINED: return "ORIENTATION_UNDEFINED";   // 0
            case Configuration.ORIENTATION_PORTRAIT: return "ORIENTATION_PORTRAIT";     // 1
            case Configuration.ORIENTATION_LANDSCAPE: return "ORIENTATION_LANDSCAPE";   // 2
            case Configuration.ORIENTATION_SQUARE: return "ORIENTATION_SQUARE";         // 3
            default: return null;
        }
    }
    @SuppressLint("DefaultLocale")
    private String getOrientationTag(final int orientation) {
        return String.format("[%d:%s]", orientation, describeOrientation(orientation).substring(12, 16).toLowerCase());
    }
}

アクティビティ.java

package xxx.android.app;

import xxx.android.ActivityCore;
import xxx.android.ActivityCore.ActivityCallbackInterface;
import android.os.Bundle;

public abstract class Activity extends android.app.Activity implements ActivityCallbackInterface {
    private final ActivityCore activityCore;

    public Activity() { super(); activityCore = new ActivityCore(this); }

    @Override
    protected void onCreate(Bundle savedInstanceState) { activityCore.onCreate(savedInstanceState); }
    @Override public void onCreateCallback(Bundle savedInstanceState) { super.onCreate(savedInstanceState); }

    @Override
    public void onBeforeSaveInstanceState(Bundle outState) {}       // Optionally: let child class override
    @Override
    protected void onSaveInstanceState(Bundle outState) { activityCore.onSaveInstanceState(outState); }
    @Override public void onSaveInstanceStateCallback(Bundle outState) { super.onSaveInstanceState(outState); }

    public final int getCurrentOrientation() { return activityCore.getCurrentOrientation(); }

    public final boolean isDevicePortrait() { return activityCore.isDevicePortrait(); }
    public final boolean isDeviceLandscape() { return activityCore.isDeviceLandscape(); }
    public final boolean isNewDevicePortrait() { return activityCore.isNewDevicePortrait(); }
    public final boolean isNewDeviceLandscape() { return activityCore.isNewDeviceLandscape(); }
    public final boolean isPortrait2Landscape() { return activityCore.isPortrait2Landscape(); }
    public final boolean isLandscape2Portrait() { return activityCore.isLandscape2Portrait(); }

    public final String describeCurrentOrientation() { return activityCore.describeCurrentOrientation(); }
    public final String getCurrentOrientationTag() { return activityCore.getCurrentOrientationTag(); }
    public final String describeNewOrientation() { return activityCore.describeNewOrientation(); }
    public final String getNewOrientationTag() { return activityCore.getNewOrientationTag(); }
}

FragmentActivity.java

package xxx.android.support.v4.app;

import xxx.android.ActivityCore;
import xxx.android.ActivityCore.ActivityCallbackInterface;
import android.os.Bundle;

public abstract class FragmentActivity extends android.support.v4.app.FragmentActivity implements ActivityCallbackInterface {
    private final ActivityCore activityCore;

    public FragmentActivity() { super(); activityCore = new ActivityCore(this); }

    @Override
    protected void onCreate(Bundle savedInstanceState) { activityCore.onCreate(savedInstanceState); }
    @Override public void onCreateCallback(Bundle savedInstanceState) { super.onCreate(savedInstanceState); }

    @Override
    public void onBeforeSaveInstanceState(Bundle outState) {}       // Optionally: let child class override
    @Override
    protected void onSaveInstanceState(Bundle outState) { activityCore.onSaveInstanceState(outState); }
    @Override public void onSaveInstanceStateCallback(Bundle outState) { super.onSaveInstanceState(outState); }

    public final int getCurrentOrientation() { return activityCore.getCurrentOrientation(); }

    public final boolean isDevicePortrait() { return activityCore.isDevicePortrait(); }
    public final boolean isDeviceLandscape() { return activityCore.isDeviceLandscape(); }
    public final boolean isNewDevicePortrait() { return activityCore.isNewDevicePortrait(); }
    public final boolean isNewDeviceLandscape() { return activityCore.isNewDeviceLandscape(); }
    public final boolean isPortrait2Landscape() { return activityCore.isPortrait2Landscape(); }
    public final boolean isLandscape2Portrait() { return activityCore.isLandscape2Portrait(); }

    public final String describeCurrentOrientation() { return activityCore.describeCurrentOrientation(); }
    public final String getCurrentOrientationTag() { return activityCore.getCurrentOrientationTag(); }
    public final String describeNewOrientation() { return activityCore.describeNewOrientation(); }
    public final String getNewOrientationTag() { return activityCore.getNewOrientationTag(); }
}

最後に、皆さんがとても助けてくれて、解決の進捗状況を私と一緒に更新し続けてくれて本当にありがとう! 皆さんは、stackoverflow をプログラマーにとって完璧なサイトにするキーパーソンです。私のコードに問題があったり、改善の余地があれば、遠慮なくまた助けてください :-)

いくつかの改善?

は使用時に実装されるためonBeforeSaveInstanceState()、3 つのクラスすべてを保持する必要がありabstractます。これにより、メンバ変数が重複しますcurrent_orientationcurrent_orientationに入れるclass ActivityBaseか、別の場所にグループ化できれば、もっといいでしょう!

愚かな私。私はそれを修正しました:-)

4

4 に答える 4

1

方向を追跡するのに役立つヘルパー メソッドを追加したいと考えています。これは、サブクラスの作成を保証するのに十分な大きさではないと思います。

代わりにヘルパー クラスに入れます。

public class OrientationHelper {
    private Activity activity;
    private int current_orientation;

    public OrientationHelper(Activity activity){
        this.activity = activity;
        orientation = Configuration.ORIENTATION_UNDEFINED;
    }

    public int getNewOrientation() { 
        return activity.getResources().getConfiguration().orientation;
    }

    // call this when you wish to update current_orientation
    public void updateOrientation() {
        current_orientation = getNewOrientation();
    }

    public int getCurrentOrientation() { 
        return current_orientation; 
    }

    public boolean isDevicePortrait() { 
        return current_orientation == Configuration.ORIENTATION_PORTRAIT; 
    }

    public boolean isDeviceLandscape() { 
        return current_orientation == Configuration.ORIENTATION_LANDSCAPE; 
    }

    public boolean isNewDevicePortrait() { 
        return getCurrentOrientation() == Configuration.ORIENTATION_PORTRAIT; 
    }

    public boolean isNewDeviceLandscape() { 
        return getCurrentOrientation() == Configuration.ORIENTATION_LANDSCAPE; 
    }

    public boolean isPortrait2Landscape() { 
        return isDevicePortrait() && isNewDeviceLandscape(); 
    }

    public boolean isLandscape2Portrait() { 
        return isDeviceLandscape() && isNewDevicePortrait(); 
    }

    public String describeCurrentOrientation() { 
        return describeOrientation(current_orientation); 
    }

    public String describeNewOrientation() { 
        return describeOrientation(getNewOrientation());
    }

    private String describeOrientation(int current_orientation) {
        switch (current_orientation) {
            case Configuration.ORIENTATION_UNDEFINED: 
                return "ORIENTATION_UNDEFINED";
            case Configuration.ORIENTATION_PORTRAIT: 
                return "ORIENTATION_PORTRAIT";
            case Configuration.ORIENTATION_LANDSCAPE: 
                return "ORIENTATION_LANDSCAPE";
            case Configuration.ORIENTATION_SQUARE: 
                return "ORIENTATION_SQUARE";
            default: return null;
        }
    }
}

向きを操作するアクティビティ (およびそれらのみ) では、OrientationHelper をインスタンス化し、選択した場所で updateOrientation() を呼び出すことができます。

インスタンス状態の保存を整理するもう 1 つのコードは、再利用できるようにするためだけに、別のクラスには入れません。これは、状態の保存に対する変更が発生すると予想される場所ではないため、見落とされる可能性があるためです。(それが何をすべきかを理解するのに少しスクロールする必要がありました。)

それを行う最も読みやすい方法は、それを使用する各アクティビティで明示的に記述することだと思います。

最後に考慮すべきことは、Sherlock Actionbar が既に Activity を拡張していることです。そして当然のことだと思います。ただし、Activity も拡張すると、問題が発生することがあります。

于 2013-04-02T21:38:55.587 に答える
1

私の見解では、ここでの最良の解決策は、ロジックをクラスに委任することです。それを と呼びましょうCustomActivityLogic

CustomActivityまた、ロジック クラスからアクティビティ クラスのデータまたはメソッドにアクセスする場合は、アクティビティ用の共通インターフェイス ( ) を作成する必要があります。

保護された仮想オーバーライド メソッドを呼び出すには、次の 2 つの解決策があります。

  • オーバーライドされたメソッドから夕食のメソッドを呼び出す
  • サブクラスに新しいメソッドを作成し、この新しいメソッドからスーパー メソッドを呼び出します。共有ロジックから新しいメソッドを呼び出します。

CustomActivity.java

public interface CustomActivity {
    void someMethod();
}

Activiti.java

import android.app.Activity

public class Activiti 
    extends Activity 
    implements CustomActivity {

    private CustomActivityLogic logic = new CustomActivityLogic();

    public void someMethod() { /***/ }

    public void myNewMethod() { logic.myNewMethod(this); }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        logic.onCreate(this, savedInstanceState); // call shared logic
        super.onCreate(savedInstanceState); // call super
    }
}

FragmentActivity.java

import android.support.v4.app.FragmentActivity;

public class FragmentActivitii 
    extends FragmentActivity 
    implements CustomActivity {

    private CustomActivityLogic logic = new CustomActivityLogic();

    public void someMethod() { /***/ }

    public void myNewMethod() { logic.myNewMethod(this); }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        logic.onCreate(this, savedInstanceState); // call shared logic
        super.onCreate(savedInstanceState); // call super
    }
}

CustomActivityLogic.java

 public class CustomActivityLogic {
     public void myNewMethod(CustomActivity activity) { /*...*/ }
     public void onCreate(Activity activity, Bundle savedInstanceState) { 
         /* shared creation logic */
     }
 }

CustomActivityインターフェイス経由で onCreate を外部から呼び出せるようにするアプローチ

CustomActivity.java

public interface CustomActivity {
    void someMethod();
    void onCreateSuper(Bundle savedInstanceState);
}

Activiti.java

import android.app.Activity

public class Activiti 
    extends Activity 
    implements CustomActivity {

    private CustomActivityLogic logic = new CustomActivityLogic();

    public void someMethod() { /***/ }

    public void myNewMethod() { logic.myNewMethod(this); }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        logic.onCreate(this, savedInstanceState); // call shared logic
    }

    public void onCreateSuper(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); // call super
    }
}

FragmentActivity.java

import android.support.v4.app.FragmentActivity;

public class FragmentActivitii 
    extends FragmentActivity 
    implements CustomActivity {

    private CustomActivityLogic logic = new CustomActivityLogic();

    public void someMethod() { /***/ }

    public void myNewMethod() { logic.myNewMethod(this); }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        logic.onCreate(this, savedInstanceState); // call shared logic
    }

    public void onCreateSuper(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); // call super
    }
}

CustomActivityLogic.java

 public class CustomActivityLogic {
     public void myNewMethod(CustomActivity activity) { /*...*/ }
     public void onCreate(CustomActivity activity, Bundle savedInstanceState) { 
         /* shared creation logic */
         activity.onCreateSuper(savedInstanceState); // call-back super
     }
 }
于 2013-04-01T23:04:10.823 に答える
0

あなたの設計上の問題は、今後の Java 8 仮想拡張機能によって対処される問題の 1 つです。詳細については、以下の URL を参照してください。

http://java.dzone.com/articles/java-8-virtual-extension

その間、簡単な方法はありません。デコレータ クラスは機能しません。代わりに、両方のクラスから呼び出されるユーティリティ クラスを実装します。

新しい情報に基づいて編集:

/** NOTE: cannot be abstract class **/
public class ActivitiBase {
   private int current_orientation = Configuration.ORIENTATION_UNDEFINED;  // ORIENTATION_UNDEFINED = 0

    private Activity activity;
    public void ActivitiBase(Activity activity) {
        this.activity = activity;
    }

   public void onCreate(Bundle savedInstanceState) {
      current_orientation = activity.getResources().getConfiguration().orientation;
   }

   public boolean isDevicePortrait() { return current_orientation == 
                      Configuration.ORIENTATION_PORTRAIT; }
   }    

   public void myNewMethod() { ... }
}

活動クラス:

public class Activiti extends Activity {
    private ActiviBase activitiBase;

    public Activiti() {
       activitiBase = new ActiviBase(this);
    }

    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      activitiBase.onCreate(savedInstanceState);
    }

    public void myNewMethod() { 
       activitiBase.myNewMethod();
    }
}

FrameActiviti クラス:

public class FrameActiviti extends FrameActivity {
    private ActiviBase activitiBase;

    public FrameActiviti() {
       activitiBase = new ActiviBase(this);
    }

    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      activitiBase.onCreate(savedInstanceState);
    }

    public void myNewMethod() { 
       activitiBase.myNewMethod();
    }
}
于 2013-04-02T00:57:33.953 に答える
0

を使ってみてはDecorator Patternどうですか?残念ながら、これには既存のすべてのメソッド、または目的に必要なメソッドを委任する必要があります。

public class ActivityDecorator extends Activity 
{
    private Activity RealActivity;
    public ActivityDecorator(Activity _realActivity)
    {
        RealActivity = _realActivity;
    }

    public void myNewMethod() { ... }  // this exposes the added/new functionality

    // unfortunately for old functionality you need to delegate
    public void oldMethod() { RealActivity.oldMethod(); }
}

ただし、クラスに対してこれを一度実行すると、あなたの場合のように派生する型でActivityProxyのインスタンスを構築できます。例えばActivityDecoratorActivityFragmentActivity

ActivityDecorator decorator = new ActivityDecorator(new FragmentActivity());
于 2013-04-01T23:05:51.237 に答える