Android用Header2ActionBarライブラリ

Android用Header2ActionBarライブラリ





(注目を集めるデモ)




Googleのアプリケーション(Playミュージック、Googleプレス)およびおそらく他のいくつかのアプリケーションでも、おそらく似たようなものを見たことがあるでしょう。 これらの目的のために、 ManuelPeinadoのライブラリFadingActionBarがかなり長い間存在しており、タスクを完全に実行しますが、残念ながら2つの「致命的な」欠点があります。



2番目の問題は既知の問題として説明されています。

既知の問題



ライブラリとリストビューには重要な問題があります。 より具体的には、構成の変更によりアクティビティが再作成された場合、物事はまったく正しく機能しません。 そのため、構成の変更を自分で処理しない限り(またはアクティビティがポートレート/ランドスケープのみ)、この問題の解決策が見つかるまでScrollViewにコンテンツを保持することを強くお勧めします。




この欠点を修正しようとして、私は実装を書くことに決め、それによって両方の欠点を除去しました:)







ライブラリは3つのファイルで構成されています。

FadingActionBarActivity.java
/** * Created by AChep@xda <artemchep@gmail.com> */ public class FadingActionBarActivity extends Activity { private static final String TAG = "FadingActionBarActivity"; private int mAlpha = 255; private Drawable mDrawable; private boolean isAlphaLocked; public void setActionBarBackgroundDrawable(Drawable drawable) { getActionBar().setBackgroundDrawable(drawable); mDrawable = drawable; if (mAlpha == 255) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) mAlpha = drawable.getAlpha(); } else { setActionBarAlpha(mAlpha); } } /** * An {@link android.app.ActionBar} background drawable. * * @see #setActionBarBackgroundDrawable(android.graphics.drawable.Drawable) * @see #setActionBarAlpha(int) */ public Drawable getActionBarBackgroundDrawable() { return mDrawable; } /** * Please use this method for global changes only! * Otherwise, please, use {@link android.graphics.drawable.Drawable#setAlpha(int)} * to {@link #getActionBarBackgroundDrawable()} directly. * * @param alpha a value from 0 to 255 * @see #getActionBarBackgroundDrawable() * @see #getActionBarAlpha() */ public void setActionBarAlpha(int alpha) { if (mDrawable == null) { Log.w(TAG, "Set action bar background before setting alpha!"); return; } if (!isAlphaLocked) mDrawable.setAlpha(alpha); mAlpha = alpha; } public int getActionBarAlpha() { return mAlpha; } public void setActionBarAlphaLocked(boolean isLocked) { isAlphaLocked = isLocked; } }
      
      



HeaderFragment .java
 /** * Little header fragment. * <p> * Created by AChep@xda <artemchep@gmail.com> * </p> */ public class HeaderFragment extends Fragment { private static final String TAG = "HeaderFragment"; private FrameLayout mRoot; private View mContentOverlay; private View mHeader; private int mHeaderHeight; private int mCurrentHeaderHeight; private int mCurrentHeaderTranslateY; private Space mFakeHeader; private boolean mListViewEmpty; private OnHeaderScrollChangeListener mOnHeaderScrollChangeListener; public interface OnHeaderScrollChangeListener { public void onHeaderScrollChanged(float progress, int height, int scroll); } public void setOnHeaderScrollChangeListener(OnHeaderScrollChangeListener listener) { mOnHeaderScrollChangeListener = listener; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final Activity activity = getActivity(); mHeader = inflater.inflate(getHeaderResource(), container, false); mHeaderHeight = mHeader.getLayoutParams().height; mCurrentHeaderHeight = mHeaderHeight; mCurrentHeaderTranslateY = 0; onPrepareHeaderView(mHeader); // Perform fake header view. mFakeHeader = new Space(activity); mFakeHeader.setLayoutParams(new ListView.LayoutParams( 0, mHeaderHeight)); View content = inflater.inflate(getContentResource(), container, false); assert content != null; if (content instanceof ListView) { final ListView listView = (ListView) content; mListViewEmpty = true; listView.addHeaderView(mFakeHeader); onPrepareContentListView(listView); listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView absListView, int i) { /* unused */ } @Override public void onScroll(AbsListView absListView, int i, int i2, int i3) { if (mListViewEmpty) { // poor poor listview :( updateHeaderScroll(0); } else { final View child = absListView.getChildAt(0); if (child == mFakeHeader) { updateHeaderScroll(child.getTop()); } else { updateHeaderScroll(-mHeaderHeight); } } } }); } else { onPrepareContentView(content); // Merge fake header view and content view final LinearLayout ll = new LinearLayout(activity); ll.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); ll.setOrientation(LinearLayout.VERTICAL); ll.addView(mFakeHeader); ll.addView(content); final NotifyingScrollView scrollView = new NotifyingScrollView(activity); scrollView.addView(ll); scrollView.setOnScrollChangedListener(new NotifyingScrollView.OnScrollChangedListener() { @Override public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) { updateHeaderScroll(-t); } }); content = scrollView; } mRoot = new FrameLayout(activity); mRoot.addView(content, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mRoot.addView(mHeader); // Overlay view always shows at the top of content. mContentOverlay = onCreateContentOverlayView(); if (mContentOverlay != null) { mRoot.addView(mContentOverlay, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); } // Initial notify notifyOnHeaderScrollChangeListener(0, mHeaderHeight, 0); return mRoot; } private void updateHeaderScroll(int scrollTo) { scrollTo = scrollTo > 0 ? 0 : scrollTo < -mHeaderHeight ? -mHeaderHeight : scrollTo; final boolean allowChangeHeight = isHeaderHeightFloating(); final int height = mHeaderHeight + scrollTo / 2; final int transY = allowChangeHeight ? scrollTo / 2 : scrollTo; if (height != mCurrentHeaderHeight && allowChangeHeight) { final ViewGroup.LayoutParams lp = mHeader.getLayoutParams(); lp.height = height; mHeader.setLayoutParams(lp); mCurrentHeaderHeight = height; } if (transY != mCurrentHeaderTranslateY) { mHeader.setTranslationY(transY); mCurrentHeaderTranslateY = transY; if (mContentOverlay != null) { final ViewGroup.LayoutParams lp = mContentOverlay.getLayoutParams(); final int delta = mHeaderHeight + scrollTo; lp.height = mRoot.getHeight() - delta; mContentOverlay.setLayoutParams(lp); mContentOverlay.setTranslationY(delta); } notifyOnHeaderScrollChangeListener((float) -scrollTo / mHeaderHeight, mHeaderHeight, -scrollTo); } } private void notifyOnHeaderScrollChangeListener(float progress, int height, int scroll) { if (mOnHeaderScrollChangeListener != null) { // Notify upper fragment to update ActionBar's alpha or whatever. mOnHeaderScrollChangeListener.onHeaderScrollChanged(progress, height, scroll); } } /** * If true, header's height might be changed on scroll. * <p>Note: It takes a lot of calculations to measure the header all the time.</p> */ public boolean isHeaderHeightFloating() { return false; } /** * Int reference to header's resource. * * @see #onPrepareHeaderView(android.view.View) * @see #getContentResource() */ public int getHeaderResource() { return 0; } /** * This is the place for setting up the header. * * @param view inflated header view. * @see #getHeaderResource() */ public void onPrepareHeaderView(View view) { /* for my child */ } /** * Int reference to content's resource. * <p> * <b>Attention</b>: Parent view must be {@link android.widget.ListView ListView} * or something else which will work inside of {@link android.widget.ScrollView ScrollView}. * Otherwise it <b>WON'T</b> work. * </p> * * @see #getHeaderResource() * @see #onPrepareContentListView(ListView) */ public int getContentResource() { return 0; } /** * Called if the content's parent is a {@link android.widget.ListView ListView}. * * @see #getContentResource() * @see #setListViewAdapter(android.widget.ListView, android.widget.ListAdapter) */ public void onPrepareContentListView(ListView listView) { /* for my child */ } public void setListViewAdapter(ListView listView, ListAdapter adapter) { mListViewEmpty = adapter == null; listView.removeHeaderView(mFakeHeader); listView.addHeaderView(mFakeHeader); listView.setAdapter(adapter); } /** * Called if the content's parent is NOT a {@link android.widget.ListView ListView}. * * @see #getContentResource() */ public void onPrepareContentView(View view) { /* for my child */ } public View onCreateContentOverlayView() { return null; } }
      
      



NotifyingScrollView .java
 /** * @author Cyril Mottier with modifications from Manuel Peinado */ public class NotifyingScrollView extends ScrollView { // Edge-effects don't mix well with the translucent action bar in Android 2.X private boolean mDisableEdgeEffects = true; /** * @author Cyril Mottier */ public interface OnScrollChangedListener { void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt); } private OnScrollChangedListener mOnScrollChangedListener; public NotifyingScrollView(Context context) { super(context); } public NotifyingScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public NotifyingScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mOnScrollChangedListener != null) { mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt); } } public void setOnScrollChangedListener(OnScrollChangedListener listener) { mOnScrollChangedListener = listener; } @Override protected float getTopFadingEdgeStrength() { // http://stackoverflow.com/a/6894270/244576 if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return 0.0f; } return super.getTopFadingEdgeStrength(); } @Override protected float getBottomFadingEdgeStrength() { // http://stackoverflow.com/a/6894270/244576 if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return 0.0f; } return super.getBottomFadingEdgeStrength(); } }
      
      







Android Studioで作成されたライブラリプロジェクトとしてGitHubを使用します。



使用する



HeaderFragmentとFadingActionBarActivityはネイティブの対応するものから継承されるため、現時点ではAndroid <4.0はそのままではサポートされていません。




このアプリケーションは、上記のスクリーンショットのデモのようになります。 したがって、アクティビティの例:

 public class MainActivity extends FadingActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //   ActionBar'a setActionBarBackgroundDrawable(getResources().getDrawable(R.drawable.actionbar_bg)); FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.container, new TestHeaderFragment() ).commit(); } }
      
      





 public class TestHeaderFragment extends HeaderFragment { @Override public void onAttach(Activity activity) { super.onAttach(activity); //   ActionBar'a    setOnHeaderScrollChangeListener(new OnHeaderScrollChangeListener() { @Override public void onHeaderScrollChanged(float progress, int height, int scroll) { height -= getActivity().getActionBar().getHeight(); progress = (float) scroll / height; if (progress > 1f) progress = 1f; ((FadingActionBarActivity) getActivity()).setActionBarAlpha((int) (255 * progress)); } }); } @Override public int getHeaderResource() { return R.layout.header; } @Override public void onPrepareHeaderView(View view) { super.onPrepareHeaderView(view); //  view  } @Override public int getContentResource() { return R.layout.content; } @Override public void onPrepareContentListView(ListView listView) { super.onPrepareContentListView(listView); //  view  setListViewAdapter(listView, new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.title, new String[]{"Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android", "Android"})); }
      
      







また、アクティビティスタイルにフラグを追加する必要があります。

 <item name="android:windowActionBarOverlay">true</item>
      
      





コンテンツをActionBarの下に配置できるようにする





事前に手で顔を覆い、コードと英語を謝罪します。 :(



All Articles