Android:浏览视图时移动背景图片

我想要的是一个背景图像,其行为像股票主屏幕或天气应用程序在这里: https : //youtube.com/watch?v= i2Oh4GL5wBE#t =0m54s

我只需要一个没有animation的背景图片(比如那个video中的道路),在滑动到另一个视图的同时滚动“一点点”。 在我的应用程序中,我想通过一些ListViews滑动背景。

我testing过的东西:

  • ViewFlipper:我使用大的图像,并垂直切成5块。 然后,我将每个布局(显示ListViews)中的切割图像设置为背景。
  • Horizo​​ntalScrollView:在Horizo​​ntalScrollView中,我添加了一个LinearLayout,并将大图像设置为背景。 然后我使用GestureDectector添加“捕捉”滑动通过ListViews。

这两个工作,但这需要一个非常大的图像,我不确定是否正确缩放到不同的屏幕尺寸。 它也不像原来的主屏幕那样,在前景变成下一个视图的时候,背景会移动一点点。

我阅读这里的post: https : //stackoverflow.com/questions/2943619/android-make-application-background-behave-like-homescreen-background

我检查了推荐的Launcher.java,这导致了我的Workspace.java:

https://android.googlesource.com/platform/packages/apps/Launcher2/+/master/src/com/android/launcher2/Workspace.java

据我所知,它通过设置偏移量来使用WallpaperManager。

mWallpaperManager.setWallpaperOffsets(getWindowToken(), Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 

API说:

setWallpaperOffsets(IBinder windowToken,float xOffset,float yOffset)设置当前壁纸在任何较大空间内的位置,当该壁纸在给定窗口后面可见时。

但据我所知,这只是改变了手机本身的背景图像。

我能做什么? 你知道一个开源的应用程序或样本代码是否也一样? 可能我必须自己画canvas(以前从未做过)。 请给我一些build议。

Solutions Collecting From Web of "Android:浏览视图时移动背景图片"

那种老问题

我实现了类似的东西,也覆盖ViewPager onDraw

我在这里发布了所有的代码

最后,我得到了它的工作(仍然需要一些修改)。 🙂

我必须承认,我无法在ViewGroup绘制背景。 那么,它的工作,但我无法控制滚动(滚动太多)。 我基本上做了什么,合并了2个教程。

  • 首先从这里开始: Android Homescreen它在不同的布局之间切换。 首先,我设置了透明背景。
  • 从这里开始的第二个:可拖动符号通过触摸(使用onDraw ,如build议)在屏幕上拖动符号。 我减less了符号到一个单一的,并将其从ImageView更改为LinearLayout

在这个布局中,我把ViewGroup ,在onTouchEventonInterceptTouchEvent做了一些小的改动,添加了一些不好的代码,最后它的工作。 🙂

如果有人感兴趣,我会清理代码并在这里发布,但是我为自己的编码风格感到羞耻,这是一团糟。 ;)

非常感谢,感谢您的帮助! 🙂

更新:所以,这里是代码:

MainActivity.java

 package de.android.projects; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } } 

MoveBackground.java

 package de.android.projects; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.LinearLayout; /* * LinearLayout which moves the background image by touchEvents. * Taken from: http://eagle.phys.utk.edu/guidry/android/DraggableSymbols.html * and changed a little bit */ public class MoveBackground extends LinearLayout{ private Drawable background; // background picture private float X = -300; // Current x coordinate, upper left corner - 300 private int scroll; public MoveBackground(Context context) { super(context); } public MoveBackground(Context context, AttributeSet attrs) { super(context); background = context.getResources().getDrawable(R.drawable.backgroundpicture); //just for tests, not really optimized yet :) background.setBounds(0,0,1000,getResources().getDisplayMetrics().heightPixels); setWillNotDraw(false); } /* * Don't need these methods, maybe later for gesture improvements */ /* @Override public boolean onInterceptTouchEvent(MotionEvent ev) { onTouchEvent(ev); return false; } @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); switch (action) { // MotionEvent class constant signifying a finger-drag event case MotionEvent.ACTION_MOVE: { // Request a redraw invalidate(); break; } // MotionEvent class constant signifying a finger-up event case MotionEvent.ACTION_UP: invalidate(); // Request redraw break; } return true; } */ // This method will be called each time the screen is redrawn. // When to redraw is under Android control, but we can request a redraw // using the method invalidate() inherited from the View superclass. @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); // get the object movement if (BadScrollHelp.getScrollX() != scroll){ //reduce the scrolling X -= scroll / 5; scroll = BadScrollHelp.getScrollX(); } // Draw background image at its current locations canvas.save(); canvas.translate(X,0); background.draw(canvas); canvas.restore(); } } 

ViewFlipper.java

 package de.android.projects; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.view.ViewConfiguration; import android.widget.Scroller; /* * Flip different views. Taken from tutorial there http://android-projects.de/2011/01/04/android-homescreen-view-flipper/ */ public class ViewFlipper extends ViewGroup { private Scroller mScroller; private VelocityTracker mVelocityTracker; private int mScrollX = 0; private int mCurrentScreen = 0; private float mLastMotionX; private static final String LOG_TAG = "DragableSpace"; private static final int SNAP_VELOCITY = 1000; private final static int TOUCH_STATE_REST = 0; private final static int TOUCH_STATE_SCROLLING = 1; private int mTouchState = TOUCH_STATE_REST; private int mTouchSlop = 0; public ViewFlipper(Context context) { super(context); mScroller = new Scroller(context); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); this.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.FILL_PARENT)); setWillNotDraw(false); requestDisallowInterceptTouchEvent(true); } public ViewFlipper(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.DragableSpace); mCurrentScreen = a.getInteger(R.styleable.DragableSpace_default_screen, 0); mScroller = new Scroller(context); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); this.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT , ViewGroup.LayoutParams.FILL_PARENT)); setWillNotDraw(false); requestDisallowInterceptTouchEvent(true); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* * This method JUST determines whether we want to intercept the motion. * If we return true, onTouchEvent will be called and we do the actual * scrolling there. */ /* * Shortcut the most recurring case: the user is in the dragging state * and he is moving his finger. We want to intercept this motion. */ final int action = ev.getAction(); if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { return true; } final float x = ev.getX(); switch (action) { case MotionEvent.ACTION_MOVE: /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check * whether the user has moved far enough from his original down touch. */ /* * Locally do absolute value. mLastMotionX is set to the y value * of the down event. */ final int xDiff = (int) Math.abs(x - mLastMotionX); boolean xMoved = xDiff > mTouchSlop + 50; if (xMoved) { // Scroll if the user moved far enough along the X axis then mTouchState = TOUCH_STATE_SCROLLING; } break; case MotionEvent.ACTION_DOWN: // Remember location of down touch mLastMotionX = x; /* * If being flinged and user touches the screen, initiate drag; * otherwise don't. mScroller.isFinished should be false when * being flinged. */ mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // Release the drag mTouchState = TOUCH_STATE_REST; break; } /* * The only time we want to intercept motion events is if we are in the * drag mode. */ return mTouchState != TOUCH_STATE_REST; } @Override public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); final int action = event.getAction(); final float x = event.getX(); switch (action) { case MotionEvent.ACTION_DOWN: Log.i(LOG_TAG, "event : down"); /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. */ if (!mScroller.isFinished()) { mScroller.abortAnimation(); } // Remember where the motion event started mLastMotionX = x; break; case MotionEvent.ACTION_MOVE: // Scroll to follow the motion event final int deltaX = (int) (mLastMotionX - x); mLastMotionX = x; if (deltaX < 0) { if (mScrollX > 0) { BadScrollHelp.setScrollX(deltaX); scrollBy(Math.max(-mScrollX, deltaX), 0); } } else if (deltaX > 0) { final int availableToScroll = getChildAt(getChildCount() - 1) .getRight() - mScrollX - getWidth(); if (availableToScroll > 0) { BadScrollHelp.setScrollX(deltaX); scrollBy(Math.min(availableToScroll, deltaX), 0); } } // Request a redraw invalidate(); break; case MotionEvent.ACTION_UP: Log.i(LOG_TAG, "event : up"); final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000); int velocityX = (int) velocityTracker.getXVelocity(); if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { // Fling hard enough to move left snapToScreen(mCurrentScreen - 1); } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { // Fling hard enough to move right snapToScreen(mCurrentScreen + 1); } else { snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } mTouchState = TOUCH_STATE_REST; //neu unten invalidate(); // Request redraw break; case MotionEvent.ACTION_CANCEL: Log.i(LOG_TAG, "event : cancel"); mTouchState = TOUCH_STATE_REST; } mScrollX = this.getScrollX(); return true; } private void snapToDestination() { final int screenWidth = getWidth(); final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; Log.i(LOG_TAG, "from des"); snapToScreen(whichScreen); } public void snapToScreen(int whichScreen) { Log.i(LOG_TAG, "snap To Screen " + whichScreen); mCurrentScreen = whichScreen; BadScrollHelp.setCurrentScreen(mCurrentScreen); final int newX = whichScreen * getWidth(); final int delta = newX - mScrollX; mScroller.startScroll(mScrollX, 0, delta, 0, Math.abs(delta) * 2); invalidate(); } public void setToScreen(int whichScreen) { Log.i(LOG_TAG, "set To Screen " + whichScreen); mCurrentScreen = whichScreen; BadScrollHelp.setCurrentScreen(mCurrentScreen); final int newX = whichScreen * getWidth(); mScroller.startScroll(newX, 0, 0, 0, 10); invalidate(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childLeft = 0; final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != View.GONE) { final int childWidth = child.getMeasuredWidth(); child.layout(childLeft, 0, childLeft + childWidth, child .getMeasuredHeight()); childLeft += childWidth; } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int width = MeasureSpec.getSize(widthMeasureSpec); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode != MeasureSpec.EXACTLY) { //throw new IllegalStateException("error mode."); } final int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY) { //throw new IllegalStateException("error mode."); } // The children are given the same width and height as the workspace final int count = getChildCount(); for (int i = 0; i < count; i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); } Log.i(LOG_TAG, "moving to screen "+mCurrentScreen); scrollTo(mCurrentScreen * width, 0); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { mScrollX = mScroller.getCurrX(); scrollTo(mScrollX, 0); postInvalidate(); } } /** * Return the parceable instance to be saved */ @Override protected Parcelable onSaveInstanceState() { final SavedState state = new SavedState(super.onSaveInstanceState()); state.currentScreen = mCurrentScreen; return state; } /** * Restore the previous saved current screen */ @Override protected void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState) state; super.onRestoreInstanceState(savedState.getSuperState()); if (savedState.currentScreen != -1) { mCurrentScreen = savedState.currentScreen; BadScrollHelp.setCurrentScreen(mCurrentScreen); } } // ========================= INNER CLASSES ============================== public interface onViewChangedEvent{ void onViewChange (int currentViewIndex); } /** * A SavedState which save and load the current screen */ public static class SavedState extends BaseSavedState { int currentScreen = -1; /** * Internal constructor * * @param superState */ SavedState(Parcelable superState) { super(superState); } /** * Private constructor * * @param in */ private SavedState(Parcel in) { super(in); currentScreen = in.readInt(); } /** * Save the current screen */ @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(currentScreen); } /** * Return a Parcelable creator */ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } } 

BadScrollHelp.java

 package de.android.projects; public class BadScrollHelp { private static int scrollX = 0; private static int currentScreen = 0; public static synchronized void setScrollX(int scroll){ scrollX = scroll; } public static synchronized void setCurrentScreen(int screen){ currentScreen = screen; } public static synchronized int getScrollX(){ return scrollX; } public static synchronized int getCurrentScreen(){ return currentScreen; } } 

main.xml中

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <de.android.projects.MoveBackground xmlns:app="http://schemas.android.com/apk/res/de.android.projects" android:id="@+id/space1" android:layout_width="fill_parent" android:layout_height="fill_parent" > <de.android.projects.ViewFlipper xmlns:app="http://schemas.android.com/apk/res/de.android.projects" android:id="@+id/space" android:layout_width="fill_parent" android:layout_height="fill_parent" app:default_screen="0" > <include android:id="@+id/left" layout="@layout/left_screen" /> <include android:id="@+id/center" layout="@layout/initial_screen" /> <include android:id="@+id/right" layout="@layout/right_screen" /> </de.android.projects.ViewFlipper> </de.android.projects.MoveBackground> </FrameLayout> 

left_screen.xml,right_screen.xml和initial_screen.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:background="#00000000" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:layout_width="100dip" android:layout_height="50dip" android:text="Button" /> </LinearLayout> </LinearLayout> 

attrs.xml(在值文件夹中)

 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="DragableSpace"> <attr name="default_screen" format="integer"/> </declare-styleable> </resources> 

所以,最后,就是这样。 onDraw方法仍然需要修改。 到目前为止,我对这个解决scheme不满意,因为我认为在ViewGroup必须可以使用onDraw方法。 但我无法弄清楚。 另外,用静态方法设置variables似乎是一个肮脏的伎俩。

如果有人能给我build议如何将这些事件从ViewFlipper传递给父ViewFlipper类,会很高兴。 或者如何将ViewFlipper绘图方法包含到ViewFlipper

我可以将ViewFlipper类整合到ViewFlipper类中,并以编程方式执行addView(viewFlipper) 。 比我不需要静态解决方法了。 🙂

实现这个最简单的方法是在传递给onDraw(Canvas)的Canvas上自己绘制图像。 这是启动器在我们引入壁纸偏移API之前所做的。

非常有用的解决schemeRainer,非常感谢,这真的很有帮助,但我注意到了

 Drawable.draw(canvas) 

相比之下,做得真的很慢

 Canvas.drawBitmap(bitmap,srcRect,dstRect,mPaint) 

试试看!