PinterestやTumblrがどのように「スワイプバック」メソッドを実装したか、誰もが考えていますか.
つまり、Pinterestでは、ニュースフィードの投稿をクリックできます。 DetailActivity
が開始され、選択した投稿の詳細が表示されるより。 [戻る]ボタンを押してニュースフィードアクティビティに戻るか、(詳細アクティビティ)を左にスワイプしてニュースフィードアクティビティに戻ることができます。
ビデオ: http://youtu.be/eVcSCWetnTA
通常はoverridePendingTransition()
を使用しますが、overridePendingTransition()
はアニメーション(_R.anim.foo
_などのリソースID)を使用します。 PinterestとTumblrは、ユーザーがスワイプジェスチャーを行った場合にのみアニメーションを開始します。また、指の動きに応じた何らかの「フレームごとのアニメーション」もサポートします。そのため、指の移動距離を追跡し、対応するパーセンテージ値への遷移をアニメーション化します。
フラグメント置換をアニメーション化するためにFragmentTransaction
で「本物のJava」アニメーション/ AnimatorSetオブジェクトを使用する方法を知っています。フラグメントではonCreateAnimator()
をオーバーライドする必要がありますが、アクティビティでそのようなものを実装する方法がわかりません。アクティビティにonCreateAnimator()
(または同様のもの)はありますか?また、今すぐアニメーションを開始しないので、スワイプの方法がわかりませんが、ウィンドウ/アクティビティ/フラグメントなどのステップごとのプロパティの変更...
助言がありますか?
編集:私はyoutubeでpinterestアプリのビデオを見つけました: http://youtu.be/eVcSCWetnTA それが実装したいものです。
PinterestはFragmentsとonCreateAnimator()
を使用して「スワイプバック」を実現しています。私のアプリは既にアクティビティにFragmentとChildFragmentsを持っているので、アクティビティにそれを実装できれば、とても簡単になります。
もう一度:スワイプジェスチャーを検出する方法を知っていますが、それは私が求めているものではありません。 YouTubeビデオを見る: http://youtu.be/eVcSCWetnTA
更新:PinterestやTumblrsの実装とまったく同じ動作ではない小さなライブラリを作成しましたが、私のアプリにとってこれは良い解決策のようです: https://github.com/sockeqwe/SwipeBack? source = c
だから私は自分で解決策を見つけたと思います:
まず第一に、Pinterestは実際に@frozenkoiのようなカスタムページトランスフォーマーでViewPagerを使用しています。 pinterestアプリで、表示ページャーのオーバーロールエッジ効果を確認できます。
@Amit Guptaは、アクティビティをスライドさせるライブラリを指しています。さまざまなナビゲーションドロワーとまったく同じコンセプトで、テーマも半透明に設定されます。彼らはレイアウトをスライドさせます。しかし、それはまさに私が探していたものではありません。なぜなら、それは一番上のアクティビティを右にスライドさせ、単にfinish()を呼び出すだけだからです。ただし、その下のアクティビティはアニメーション化されません。
解決策は、アニメーションオブジェクトを使用して独自のアニメーションを作成し、それを段階的にアニメーション化することです(これはTumblrが行っていたと思います)。これは ActivityOptions で実行できます。 私の意見では、これが解決策になります。
探している効果は、Android開発者のWebサイトの ViewPager のサンプルの1つであるようです。
http://developer.Android.com/training/animation/screen-slide.html#depth-page をDepthページトランスフォーマーセクション。ビデオとソースコードがあります。
ViewPager.PageTransformer を使用すると、ページを切り替えるときのページの動作を決定できます。
サンプルとリンク先のビデオの唯一の違いは、左右が逆になっているように見えることですが、質問でリンクされている YouTubeビデオ で見たものの良い出発点になるはずです。 2つのビューのアクションを交換する必要があります。このコードに示されているように(mPager.setPageTransformer
はreverseDrawingOrder
= falseでなければなりません。中央の2つのif
セクションは交換され、position
変数はスイッチ側とは少し異なる方法で処理されることに注意してください。弾力のある効果は演習として残されます。それを入手したら共有してください!
package com.example.Android.animationsdemo;
import Android.support.v4.view.ViewPager;
import Android.view.View;
public class SinkPageTransformer implements ViewPager.PageTransformer {
private static float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Fade the page out.
view.setAlpha(1 + position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else if (position <= 1) { // (0,1]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
また、サンプルのあるページがだまされた場合に備えて、そのセクションの元のコードを次に示します。
public class DepthPageTransformer implements ViewPager.PageTransformer {
private static float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
更新:このプロジェクトのメモリ使用量の問題を修正し、スライドバックスタイルをiOSのように変更しました。
Pinterestやtumblrのようなデモを作成しました。BaseActivityを拡張するだけで、スワイプバック効果が得られ、スムーズに動作します。
これを確認してください: https://github.com/chenjishi/SlideActivity
そしてスクリーンショット:
PinterestのようなSwipeBackに基づいたGitHubプロジェクトを見つけました。
それは本当にあなたの問題を解決するべき素晴らしいオープンソースプロジェクトです。必要に応じて、戻るキーまたは単純なスワイプを押して前の画面に移動します。このプロジェクトにはオプションがあるので
1。左から右にスワイプ
2。右から左にスワイプ
3。下から上へスワイプ
https://github.com/Issacw0ng/SwipeBackLayout
また、このデモアプリケーションをGoogle Playからインストールします。
https://play.google.com/store/apps/details?id=me.imid.swipebacklayout.demo
添付のスクリーンショット:-
これがお役に立てば幸いです。
私は15分でこれを行うことができました、それはスタートにとって悪くありません。少し時間をかけると、最適化できる場合があります。
package mobi.sherif.activitydrag;
import Android.app.Activity;
import Android.os.Bundle;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.MotionEvent;
import Android.view.View;
import Android.view.animation.AlphaAnimation;
import Android.view.animation.Animation;
import Android.view.animation.Animation.AnimationListener;
import Android.view.animation.AnimationSet;
import Android.view.animation.AnimationUtils;
import Android.view.animation.LinearInterpolator;
import Android.view.animation.TranslateAnimation;
import Android.widget.FrameLayout.LayoutParams;
public class MainActivity extends Activity {
private static final double PERCENT_OF_SCROLL_OF_ACTIVITY_TO_FINISH = 0.3;
View mView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
setContentView(mView);
}
private boolean isDragging = false;
int startX;
int currentX;
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.v("sherif", isDragging?"YES":"NO" + ": " + event.getX());
if(!isDragging) {
if(event.getAction() == MotionEvent.ACTION_DOWN && event.getX()<24) {
isDragging = true;
startX = (int) event.getX();
currentX = 0;
return true;
}
return super.onTouchEvent(event);
}
switch(event.getAction()) {
case MotionEvent.ACTION_MOVE:
currentX = (int) event.getX() - startX;
LayoutParams params = (LayoutParams) mView.getLayoutParams();
params.leftMargin = currentX;
params.rightMargin = -1 * currentX;
mView.requestLayout();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
isDragging = false;
double currentPercent1 = (double) currentX / mView.getWidth();
float currentPercent = (float) currentPercent1;
if(currentX > PERCENT_OF_SCROLL_OF_ACTIVITY_TO_FINISH * mView.getWidth()) {
AnimationSet animation = new AnimationSet(false);
Animation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f - currentPercent, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
anim.setDuration(getResources().getInteger(Android.R.integer.config_mediumAnimTime));
anim.setInterpolator(new LinearInterpolator());
anim.setStartTime(AnimationUtils.currentAnimationTimeMillis());
animation.addAnimation(anim);
anim = new AlphaAnimation(1.0f, 0.5f);
anim.setDuration(getResources().getInteger(Android.R.integer.config_shortAnimTime));
anim.setInterpolator(new LinearInterpolator());
anim.setStartTime(AnimationUtils.currentAnimationTimeMillis());
animation.addAnimation(anim);
animation.setFillAfter(true);
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
finish();
}
});
mView.startAnimation(animation);
}
else {
AnimationSet animation = new AnimationSet(false);
Animation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f -1 * currentPercent, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
anim.setDuration(getResources().getInteger(Android.R.integer.config_shortAnimTime));
anim.setInterpolator(new LinearInterpolator());
anim.setStartTime(AnimationUtils.currentAnimationTimeMillis());
animation.addAnimation(anim);
animation.setFillAfter(true);
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
LayoutParams params = (LayoutParams) mView.getLayoutParams();
params.leftMargin = 0;
params.rightMargin = 0;
mView.requestLayout();
mView.clearAnimation();
}
});
mView.startAnimation(animation);
}
break;
}
return true;
}
}
階層ビューアーで確認しました。彼らはViewPagerを使用して、以前のアクティビティのスクリーンショットを持っているようです。
次のことをお勧めします。
まず、ユーザーがデバイスで行っているジェスチャーを検出します。 このリンク を参照できます
上記のリンクから関連するコードをコピーするつもりはありません。受け入れられた答えだと思います
第二に、あなたはこの方法でできます
public void onSwipeLeft() {
Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show();
}
この質問 で提案されているように、以下を実行します
彼らはそこで、アニメーションでアクティビティを終了することについて話します
Look into doing it through a theme. You can define enter exit animations for activities or the entire application
これがあなたを助けることを願っています
私はプロジェクトを書きました。フラグメントによってナビゲートされるアプリを簡単に開発でき、Pinterestのように機能します。
https://github.com/fengdai/FragmentMaster
たぶんそれはあなたが望む答えではありません。しかし、私はそれが他の誰かに役立つことを願っています。
私は現在取り組んでいるプロジェクトでこれを扱っていて、次のコードを思いつきました。今はあなたには関係ないかもしれませんが、この投稿で新しい人を助けるかもしれません。 :)
基本的には、回答で述べたようにViewPagerの実装ですが、質問に対する最も簡単で迅速なソリューションだと思います。短所は、フラグメントのみ(オブジェクトの場合は簡単に変更できます)であり、ActionBarをスワイプに追加する場合は、おそらくカスタムのものを作成することになります。
public class DoubleViewPager extends FrameLayout implements ViewPager.OnPageChangeListener {
/**
* Represents number of objects in DelegateViewPager. In others words it stands for one main content
* window and one content detail window
*/
private static final int SCREEN_COUNT = 2;
private static final int CONTENT_SCREEN = 0;
private static final int DETAIL_SCREEN = 1;
private DelegateViewPager delegateViewPager;
private SparseArray<Fragment> activeScreens = new SparseArray<Fragment>(SCREEN_COUNT) ;
private DelegateAdapter adapter;
public DoubleViewPager(Context context) {
this(context, null);
}
public DoubleViewPager(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DoubleViewPager(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
delegateViewPager = new DelegateViewPager(context);
delegateViewPager.setId(R.id.main_page_id);
delegateViewPager.setOverScrollMode(ViewPager.OVER_SCROLL_NEVER);
final FrameLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER);
addView(delegateViewPager, params);
}
/**
* Create a new PagerAdapter and set content fragment as a first object in ViewPager;
* @param fragment Fragment you want to use as a main content
* @param fm FragmentManager required for ViewPager transactions
*/
public void initialize(final Fragment fragment, final FragmentManager fm) {
adapter = new DelegateAdapter(fm);
delegateViewPager.setAdapter(adapter);
activeScreens.put(CONTENT_SCREEN, fragment);
adapter.notifyDataSetChanged();
}
/**
* Adds fragment to stack and set it as current selected item. Basically it the same thing as calling
* startActivity() with some transitions effects
* @param fragment Fragment you want go into
*/
public void openDetailScreen(Fragment fragment) {
activeScreens.put(DETAIL_SCREEN, fragment);
adapter.notifyDataSetChanged();
delegateViewPager.setCurrentItem(1, true);
}
public void hideDetailScreen() {
delegateViewPager.setCurrentItem(CONTENT_SCREEN);
if (activeScreens.get(DETAIL_SCREEN) != null) {
activeScreens.remove(DETAIL_SCREEN);
adapter.notifyDataSetChanged();
}
}
@Override
public void onPageScrolled(int i, float v, int i2) {
// unused
}
@Override
public void onPageSelected(int i) {
if (i == CONTENT_SCREEN) hideDetailScreen();
}
@Override
public void onPageScrollStateChanged(int i) {
// unused
}
private class DelegateViewPager extends ViewPager {
public DelegateViewPager(Context context) {
super(context);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return getCurrentItem() != CONTENT_SCREEN && super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return getCurrentItem() != CONTENT_SCREEN && super.onTouchEvent(event);
}
}
private final class DelegateAdapter extends FragmentPagerAdapter {
public DelegateAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
return activeScreens.get(i);
}
@Override
public int getCount() {
return activeScreens.size();
}
}
}
これは、別のViewPagerでSlidingMenuとして実装するアクティビティです。 (追加として)
public class DoubleViewPagerActivity extends FragmentActivity {
DoubleViewPager doubleViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_double_view_pager);
doubleViewPager = (DoubleViewPager) findViewById(R.id.doublePager);
doubleViewPager.initialize(new MainContentFragment(), getSupportFragmentManager());
}
public static final class MainContentFragment extends Fragment {
public MainContentFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_doublepager_content_window, parent, false);
final ViewPager pager = (ViewPager) view.findViewById(R.id.contentPager);
pager.setAdapter(new SimpleMenuAdapter(getChildFragmentManager()));
pager.setOffscreenPageLimit(2);
pager.setCurrentItem(1);
return view;
}
}
public static final class SimpleMenuAdapter extends FragmentPagerAdapter {
public SimpleMenuAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
return DoubleViewPagerActivity.PagerFragment.instance(i);
}
@Override
public int getCount() {
return 3;
}
@Override
public float getPageWidth(int position) {
switch (position) {
case 0:
case 2:
return 0.7f;
}
return super.getPageWidth(position);
}
}
public static final class PagerFragment extends Fragment {
public static PagerFragment instance(int position) {
final PagerFragment fr = new PagerFragment();
Bundle args = new Bundle();
args.putInt("position", position);
fr.setArguments(args);
return fr;
}
public PagerFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
final FrameLayout fl = new FrameLayout(getActivity());
fl.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
int position = getArguments().getInt("position");
switch (position) {
case 0:
fl.setBackgroundColor(Color.RED);
break;
case 1:
fl.setBackgroundColor(Color.GREEN);
initListView(fl);
break;
case 2:
fl.setBackgroundColor(Color.BLUE);
break;
}
return fl;
}
private void initListView(FrameLayout fl) {
int max = 50;
final ArrayList<String> items = new ArrayList<String>(max);
for (int i = 1; i <= max; i++) {
items.add("Items " + i);
}
ListView listView = new ListView(getActivity());
fl.addView(listView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
listView.setAdapter(new ArrayAdapter<String>(getActivity(),
Android.R.layout.simple_list_item_1, items));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
((DoubleViewPagerActivity) getActivity()).doubleViewPager.openDetailScreen(new DetailFragment());
}
});
}
}
public final static class DetailFragment extends Fragment {
public DetailFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
FrameLayout l = new FrameLayout(getActivity());
l.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
l.setBackgroundColor(getResources().getColor(Android.R.color.holo_purple));
return l;
}
}
}