Библиотека Header2ActionBar для Android

от автора

Библиотека Header2ActionBar для Android

(демо для привлечения внимания)

Вы, наверное, уже видели похожее в приложениях от Google (Play Музыка, Google Пресса) и, возможно, каких-либо других. Для этих целей уже довольно давно существует библиотека от ManuelPeinadoFadingActionBar, которая прекрасно выполняет свою задачу, но к сожалению, имеет два «фатальных» недостатка.

Второй из них описан как известная проблема:

Known Issues

There is an important issue with the library and ListViews. More specifically, things don’t work quite right when the activity is re-created due to a configuration change. So, unless you handle configuration changes yourself (or your activity is portrait/landscape only), I strongly suggest you stick to having your content in a ScrollView until a solution to this issue is found.

Стараясь исправить этот недостаток, я решил написать свою реализацию, тем самым устранив и оба недостатка 🙂



Библиотека состоит из трёх файлов:

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 View mHeader;     private int mHeaderHeight;     private int mCurrentHeaderHeight;     private int mCurrentHeaderTranslateY;      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);          View content = inflater.inflate(getContentResource(), container, false);         assert content != null;         if (content instanceof ListView) {             final ListView listView = (ListView) content;              // Perform fake header view.             final Space listFakeHeader = new Space(activity);             listFakeHeader.setLayoutParams(new ListView.LayoutParams(                     0, mHeaderHeight));              onPrepareContentListView(listView);             listView.addHeaderView(listFakeHeader);             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) {                     final View child = absListView.getChildAt(0);                     if (child == listFakeHeader) {                         updateHeaderScroll(child.getTop());                     } else {                         updateHeaderScroll(-mHeaderHeight);                     }                 }             });         } else {             onPrepareContentView(content);              final NotifyingScrollView scrollView = new NotifyingScrollView(activity);             scrollView.addView(content);             scrollView.setOnScrollChangedListener(new NotifyingScrollView.OnScrollChangedListener() {                 @Override                 public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {                     updateHeaderScroll(-t);                 }             });             content = scrollView;         }          final FrameLayout root = new FrameLayout(activity);         root.addView(content, new FrameLayout.LayoutParams(                 ViewGroup.LayoutParams.MATCH_PARENT,                 ViewGroup.LayoutParams.MATCH_PARENT));         root.addView(mHeader);         return root;     }      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 (mOnHeaderScrollChangeListener != null) {                 // Notify upper fragment to update ActionBar's alpha or whatever.                 int scroll = Math.abs(scrollTo);                 mOnHeaderScrollChangeListener.onHeaderScrollChanged(                         (float) scroll / mHeaderHeight, mHeaderHeight, 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()      */     public void onPrepareContentListView(ListView listView) { /* for my child */ }      /**      * Called if the content's parent is NOT a {@link android.widget.ListView ListView}.      *      * @see #getContentResource()      */     public void onPrepareContentView(View view) { /* for my child */ }  }
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();     } }

и лежит на GitHub‘е как проект библиотеки созданной в Android Studio.

Использование

HeaderFragment и FadingActionBarActivity наследуются от нативных собратьев, так что пока о Android < 4.0 можно забыть.
Наше приложение будет подобием демо на скриншоте сверху. Итак, пример Activity:

 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 контентом         listView.setAdapter(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"}));     }

Я заранее закрываю свое лицо руками и прошу прощения за свой код и английский. 🙁

ссылка на оригинал статьи http://habrahabr.ru/post/204940/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *