安卓:移动背景图片,同时通过浏览导航 [英] Android: Moving background image while navigating through Views

查看:269
本文介绍了安卓:移动背景图片,同时通过浏览导航的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

寻找天找到我目前的问题解决后,我解决你。 :)

After searching for days to find a solution for my current problem, I'm addressing to you. :)

我希望有一个背景图像,其行为类似于股票主屏幕或这里的天气应用程序:youtube.com/watch?v=i2Oh4GL5wBE#t=0m54s

What I want to have is a background image, which behaves like the stock homescreen or the weather app here: youtube.com/watch?v=i2Oh4GL5wBE#t=0m54s

我需要的只是一个没有动画背景图片(像在视频的道路),其滚动有点,而刷卡另一种观点。 在我的应用程序,我想通过一些列表视图刷卡用滚动的背景。

I just need a not animated background image (like the road in that video) which scrolls "a bit" while swiping to another view. In my app, I would like to swipe through some ListViews with a scrolling background.

我测试过的:

  • ViewFlipper:我用大图像和剪垂直插入5件。然后,我在每个版面(显示列表视图)作为背景设置这些图像切边。
  • Horizo​​ntalScrollView:在Horizo​​ntalScrollView我加了的LinearLayout,并设置大图作为背景。然后我用GestureDectector添加噼噼啪啪通过列表视图刷卡的时候。

都工作,但是这需要一个非常大的形象,我不知道这是否是正确的扩展到不同的屏幕尺寸。此外,它并不像原来的主屏幕,在后台移动只是一点点,而前景切换到下一个视图。

Both worked, but that requires a really big image and I'm not sure if it scales correctly to different screen sizes. Also it doesn't behave like the original homescreen, where the background moves just a little bit while the foreground changes to the next View.

我在这里阅读后:<一href="http://stackoverflow.com/questions/2943619/android-make-application-background-behave-like-homescreen-background">http://stackoverflow.com/questions/2943619/android-make-application-background-behave-like-homescreen-background

我查了建议Launcher.java害得我到Workspace.java

I checked the recommended Launcher.java which led me to the Workspace.java

<一个href="http://android.git.kernel.org/?p=platform/packages/apps/Launcher2.git;a=blob;f=src/com/android/launcher2/Workspace.java">http://android.git.kernel.org/?p=platform/packages/apps/Launcher2.git;a=blob;f=src/com/android/launcher2/Workspace.java

据我了解,它采用了WallpaperManager通过设置偏移。

As far as I understand, it uses the WallpaperManager by setting Offsets.

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

API说道:

API says:

setWallpaperOffsets(的IBinder   windowToken,浮xOffset,浮法   yOffset)   设置的位置   在任何较大的电流壁纸   空间,当壁纸可见   后面给定窗口。

setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) Set the position of the current wallpaper within any larger space, when that wallpaper is visible behind the given window.

不过,据我了解,这只是改变手机本身。

But as far as I understand, this just changes the background image of the phone itself.

我能做些什么?你知道一个开源应用程序或样品code这不一样吗? 也许我要画自己的画布(从来没有过)。请给我一些建议。 :)

What can I do? Do you know an open source application or sample code which does the same? Probably I have to draw Canvas myself (never did before). Please give me some advice. :)

推荐答案

最后,我得到它的工作(还是需要一些modifcations)。 :)

Finally, I got it working (still need some modifcations). :)

我必须承认,我是不是能够做到画中的ViewGroup本身的背景。那么,它的工作,但我无法控制的滚动(滚动太多)。 我主要做,被合并2教程。

I must admit that I wasn't able to do draw the background in the ViewGroup itself. Well, it worked, but I couldn't control the scrolling (scrolled too much). What I basically did, was merging 2 tutorials.

  • 从这里开始,首之一:的Andr​​oid主屏幕 它切换不同的布局之间。所以首先,我设置透明背景。
  • 第二个从这里:可拖动符号 它伤smybols了通过触摸屏(使用OnDraw中,所建议)。我减少符号提供给单之一,并从ImageView的改变它的LinearLayout。
  • First one from here: Android Homescreen It switches between different layouts. So first of all, I set transparent backgrounds.
  • Second one from here: Draggable Symbols It drags smybols over the screen by touch (using the onDraw, as suggested). I reduced the symbols to a single one and changed it from ImageView to LinearLayout.

在该布局我把ViewGroup中,确实在的onTouchEvent和onInterceptTouchEvent一些小的改动,增加了一些不好的code,最后它的工作。 :)

In that Layout I put the ViewGroup, did some little changes in the onTouchEvent and onInterceptTouchEvent, added some bad code and at last it worked. :)

如果某人的兴趣,我会收拾code和张贴在这里,但我很惭愧,我的编码风格,它是一个烂摊子。 ;)

If someone's interested, I'll clean up the code and post it here, but I'm ashamed of my coding style, it's a mess. ;)

非常感谢你,我的AP preciate您的帮助! :)

Thank you very much, I appreciate your help! :)

更新: 所以,这里是code:

UPDATE: So, here is the code:

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 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
                    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的方法仍然需要一些修改。 我不满意这个解决方案,到目前为止,因为它必须是可以使用的OnDraw方法的ViewGroup中,我想。但我无法找到答案。 此外,与静态方法设置变量,似乎是一个肮脏的把戏。

So, finally, that's it. The onDraw method still needs some modification. I'm unhappy with this solution so far, because it must be possible to use the onDraw method in the ViewGroup, I think. But I couldn't figure it out. Also, setting variables with static methods seems to be a dirty trick.

。将很高兴,如果有人可以给我建议如何从ViewFlipper这些事件传递给父MoveBackground类。或者如何包括MoveBackground画法入ViewFlipper。

Would be glad if someone could give me advice how to pass those events from the ViewFlipper to the parent MoveBackground class. Or how to include the MoveBackground drawing method into the ViewFlipper.

我可以整合ViewFlipper类到MoveBackground类,做一个addView(viewFlipper)编程。比起我不需要静态的解决方法了。 :)

I could integrate the ViewFlipper class into the MoveBackground class and do a addView(viewFlipper) programmatically. Than I would not need that static workaround anymore. :)

这篇关于安卓:移动背景图片,同时通过浏览导航的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆