2017-03-06 7 views
0

加速度計と視差効果を作成するカスタムビューが必要です。ライブラリを使用しない加速度計の視差

加速度計の値を聞くカスタムビューがありますが、どのようにこれらの値を使用してビューを適切に移動できますか?

コード:

public class ParallaxView extends AppCompatImageView 
     implements SensorEventListener { 

    private static final int SENSOR_DELAY = SensorManager.SENSOR_DELAY_FASTEST; 

    //... 

    public ParallaxView(Context context) { 
     super(context); 
    } 

    public ParallaxView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public ParallaxView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    public void init() { 
     WindowManager windowManager = (WindowManager) getContext().getSystemService(WINDOW_SERVICE); 
     mDisplay = windowManager.getDefaultDisplay(); 
     mSensorManager = (SensorManager) getContext().getSystemService(SENSOR_SERVICE); 
     mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 
    } 

    public void setNewPosition(
      @Nullable Float sensorX, 
      @Nullable Float sensorY) { 
     // ??? 
    } 

    //... 

    @Override 
    public void onSensorChanged(SensorEvent event) { 
     if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){ 
      setNewPosition(event.values[0], event.values[1]); 
     } 
    } 

    @Override 
    public void onAccuracyChanged(Sensor sensor, int i) { 

    } 

    public void registerSensorListener() { 
     mSensorManager.registerListener(this, mAccelerometer, SENSOR_DELAY); 
    } 

    public void unregisterSensorListener() { 
     mSensorManager.unregisterListener(this); 
    } 
} 

使用活動でこのビュー:事前に

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    //... 
    mParallaxView.init(); 
} 

@Override 
protected void onResume() { 
    mParallaxView.registerSensorListener(); 
    super.onResume(); 
} 

@Override 
protected void onPause() { 
    mParallaxView.unregisterSensorListener(); 
    super.onPause(); 
} 

おかげでここ

+0

は、ライブラリをご覧ください。私はすばらしいレポ*素晴らしいアンドロイドです。だから、単に作業カスタムウィジェットを取り、入力をアクセラレータ信号に変更してください –

+0

なぜライブラリがないのですか?なぜホイールを作り直すのですか – MichaelStoddart

+0

私は自分のカスタムビューを好むですが、ライブラリを提案することができます@MichaelStoddart – MarcGV

答えて

1

Activityクラスにこれを追加最後に、私は私が望む結果を得るために私のカスタムビューを作成しました。ここで

リポジトリ:ここhttps://github.com/GVMarc/ParallaxView

コード:

public class ParallaxView extends AppCompatImageView implements SensorEventListener { 

    private static final int DEFAULT_SENSOR_DELAY = SensorManager.SENSOR_DELAY_FASTEST; 
    public static final int DEFAULT_MOVEMENT_MULTIPLIER = 3; 
    public static final int DEFAULT_MIN_MOVED_PIXELS = 1; 
    private static final float DEFAULT_MIN_SENSIBILITY = 0; 

    private float mMovementMultiplier = DEFAULT_MOVEMENT_MULTIPLIER; 
    private int mSensorDelay = DEFAULT_SENSOR_DELAY; 
    private int mMinMovedPixelsToUpdate = DEFAULT_MIN_MOVED_PIXELS; 
    private float mMinSensibility = DEFAULT_MIN_SENSIBILITY; 

    private float mSensorX; 
    private float mSensorY; 
    private Float mFirstSensorX; 
    private Float mFirstSensorY; 
    private Float mPreviousSensorX; 
    private Float mPreviousSensorY; 

    private float mTranslationX = 0; 
    private float mTranslationY = 0; 

    private SensorManager mSensorManager; 
    private Sensor mAccelerometer; 

    public enum SensorDelay { 
     FASTEST, 
     GAME, 
     UI, 
     NORMAL 
    } 

    public ParallaxView(Context context) { 
     super(context); 
    } 

    public ParallaxView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public ParallaxView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    public void init() { 
     mSensorManager = (SensorManager) getContext().getSystemService(SENSOR_SERVICE); 
     mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 
    } 

    private void setNewPosition() { 
     int destinyX = (int) ((mFirstSensorX - mSensorX) * mMovementMultiplier); 
     int destinyY = (int) ((mFirstSensorY - mSensorY) * mMovementMultiplier); 

     calculateTranslationX(destinyX); 
     calculateTranslationY(destinyY); 
    } 

    private void calculateTranslationX(int destinyX) { 
     if (mTranslationX + mMinMovedPixelsToUpdate < destinyX) 
      mTranslationX++; 
     else if (mTranslationX - mMinMovedPixelsToUpdate > destinyX) 
      mTranslationX--; 
    } 

    private void calculateTranslationY(int destinyY) { 
     if (mTranslationY + mMinMovedPixelsToUpdate < destinyY) 
      mTranslationY++; 
     else if (mTranslationY - mMinMovedPixelsToUpdate > destinyY) 
      mTranslationY--; 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 
     setTranslationX(mTranslationX); 
     setTranslationY(mTranslationY); 
     invalidate(); 
    } 

    @Override 
    public void onSensorChanged(SensorEvent event) { 
     if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { 
      mSensorX = event.values[0]; 
      mSensorY = -event.values[1]; 

      manageSensorValues(); 
     } 
    } 

    private void manageSensorValues() { 
     if (mFirstSensorX == null) 
      setFirstSensorValues(); 

     if (mPreviousSensorX == null || isSensorValuesMovedEnough()) { 
      setNewPosition(); 
      setPreviousSensorValues(); 
     } 
    } 

    private void setFirstSensorValues() { 
     mFirstSensorX = mSensorX; 
     mFirstSensorY = mSensorY; 
    } 

    private void setPreviousSensorValues() { 
     mPreviousSensorX = mSensorX; 
     mPreviousSensorY = mSensorY; 
    } 

    private boolean isSensorValuesMovedEnough() { 
     return mSensorX > mPreviousSensorX + mMinSensibility || 
       mSensorX < mPreviousSensorX - mMinSensibility || 
       mSensorY > mPreviousSensorY + mMinSensibility || 
       mSensorY < mPreviousSensorX - mMinSensibility; 
    } 

    public void registerSensorListener() { 
     mSensorManager.registerListener(this, mAccelerometer, mSensorDelay); 
    } 

    public void registerSensorListener(SensorDelay sensorDelay) { 
     switch (sensorDelay) { 
      case FASTEST: 
       mSensorDelay = SensorManager.SENSOR_DELAY_FASTEST; 
       break; 
      case GAME: 
       mSensorDelay = SensorManager.SENSOR_DELAY_GAME; 
       break; 
      case UI: 
       mSensorDelay = SensorManager.SENSOR_DELAY_UI; 
       break; 
      case NORMAL: 
       mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL; 
       break; 
     } 
     registerSensorListener(); 
    } 

    public void unregisterSensorListener() { 
     mSensorManager.unregisterListener(this); 
    } 

    public void setMovementMultiplier(float multiplier) { 
     mMovementMultiplier = multiplier; 
    } 

    public void setMinimumMovedPixelsToUpdate(int minMovedPixelsToUpdate) { 
     mMinMovedPixelsToUpdate = minMovedPixelsToUpdate; 
    } 

    public void setMinimumSensibility(int minSensibility) { 
     mMinSensibility = minSensibility; 
    } 

    @Override 
    public void onAccuracyChanged(Sensor sensor, int i) { 
    } 
} 
2

は私が前に使用しているParallaxImageViewクラスです:

import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Matrix; 
import android.hardware.Sensor; 
import android.hardware.SensorEvent; 
import android.hardware.SensorEventListener; 
import android.hardware.SensorManager; 
import android.util.AttributeSet; 
import android.widget.ImageView; 

import yourpackagename.R; 

/** 
* I did not write this, I just cant remember where I got it from and thought it could be useful for others 
*/ 
public class ParallaxImageView extends ImageView implements SensorEventListener { 

private static final String TAG = ParallaxImageView.class.getName(); 

/** 
* If the x and y axis' intensities are scaled to the image's aspect ratio (true) or 
* equal to the smaller of the axis' intensities (false). If true, the image will be able to 
* translate up to it's view bounds, independent of aspect ratio. If not true, 
* the image will limit it's translation equally so that motion in either axis results 
* in proportional translation. 
*/ 
private boolean mScaledIntensities = false; 

/** 
* The intensity of the parallax effect, giving the perspective of depth. 
*/ 
private float mParallaxIntensity = 1.15f; 

/** 
* The maximum percentage of offset translation that the image can move for each 
* sensor input. Set to a negative number to disable. 
*/ 
private float mMaximumJump = .1f; 

// Instance variables used during matrix manipulation. 
private SensorInterpreter mSensorInterpreter; 
private SensorManager mSensorManager; 
private Matrix mTranslationMatrix; 
private float mXTranslation; 
private float mYTranslation; 
private float mXOffset; 
private float mYOffset; 

public ParallaxImageView(Context context) { 
    this(context, null); 
} 

public ParallaxImageView(Context context, AttributeSet attrs) { 
    this(context, attrs, 0); 
} 

public ParallaxImageView(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 

    // Instantiate future objects 
    mTranslationMatrix = new Matrix(); 
    mSensorInterpreter = new SensorInterpreter(); 

    // Sets scale type 
    setScaleType(ScaleType.MATRIX); 

    // Set available attributes 
    if (attrs != null) { 
     final TypedArray customAttrs = context.obtainStyledAttributes(attrs, R.styleable.ParallaxImageView); 

     if (customAttrs != null) { 
      if (customAttrs.hasValue(R.styleable.ParallaxImageView_intensity)) 
       setParallaxIntensity(customAttrs.getFloat(R.styleable.ParallaxImageView_intensity, mParallaxIntensity)); 

      if (customAttrs.hasValue(R.styleable.ParallaxImageView_scaledIntensity)) 
       setScaledIntensities(customAttrs.getBoolean(R.styleable.ParallaxImageView_scaledIntensity, mScaledIntensities)); 

      if (customAttrs.hasValue(R.styleable.ParallaxImageView_tiltSensitivity)) 
       setTiltSensitivity(customAttrs.getFloat(R.styleable.ParallaxImageView_tiltSensitivity, 
         mSensorInterpreter.getTiltSensitivity())); 

      if (customAttrs.hasValue(R.styleable.ParallaxImageView_forwardTiltOffset)) 
       setForwardTiltOffset(customAttrs.getFloat(R.styleable.ParallaxImageView_forwardTiltOffset, 
         mSensorInterpreter.getForwardTiltOffset())); 

      customAttrs.recycle(); 
     } 
    } 

    // Configure matrix as early as possible by posting to MessageQueue 
    post(new Runnable() { 
     @Override 
     public void run() { 
      configureMatrix(); 
     } 
    }); 
} 

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    configureMatrix(); 
} 

/** 
* Sets the intensity of the parallax effect. The stronger the effect, the more distance 
* the image will have to move around. 
* 
* @param parallaxIntensity the new intensity 
*/ 
public void setParallaxIntensity(float parallaxIntensity) { 
    if (parallaxIntensity < 1) 
     throw new IllegalArgumentException("Parallax effect must have a intensity of 1.0 or greater"); 

    mParallaxIntensity = parallaxIntensity; 
    configureMatrix(); 
} 

/** 
* Sets the parallax tilt sensitivity for the image view. The stronger the sensitivity, 
* the more a given tilt will adjust the image and the smaller needed tilt to reach the 
* image bounds. 
* 
* @param sensitivity the new tilt sensitivity 
*/ 
public void setTiltSensitivity(float sensitivity) { 
    mSensorInterpreter.setTiltSensitivity(sensitivity); 
} 

/** 
* Sets the forward tilt offset dimension, allowing for the image to be 
* centered while the phone is "naturally" tilted forwards. 
* 
* @param forwardTiltOffset the new tilt forward adjustment 
*/ 
public void setForwardTiltOffset(float forwardTiltOffset) { 
    if (Math.abs(forwardTiltOffset) > 1) 
     throw new IllegalArgumentException("Parallax forward tilt offset must be less than or equal to 1.0"); 

    mSensorInterpreter.setForwardTiltOffset(forwardTiltOffset); 
} 

/** 
* Sets whether translation should be limited to the image's bounds or should be limited 
* to the smaller of the two axis' translation limits. 
* 
* @param scaledIntensities the scaledIntensities flag 
*/ 
public void setScaledIntensities(boolean scaledIntensities) { 
    mScaledIntensities = scaledIntensities; 
} 

/** 
* Sets the maximum percentage of the image that image matrix is allowed to translate 
* for each sensor reading. 
* 
* @param maximumJump the new maximum jump 
*/ 
public void setMaximumJump(float maximumJump) { 
    mMaximumJump = maximumJump; 
} 

/** 
* Sets the image view's translation coordinates. These values must be between -1 and 1, 
* representing the transaction percentage from the center. 
* 
* @param x the horizontal translation 
* @param y the vertical translation 
*/ 
private void setTranslate(float x, float y) { 
    if (Math.abs(x) > 1 || Math.abs(y) > 1) 
     throw new IllegalArgumentException("Parallax effect cannot translate more than 100% of its off-screen size"); 

    float xScale, yScale; 

    if (mScaledIntensities) { 
     // Set both scales to their offset values 
     xScale = mXOffset; 
     yScale = mYOffset; 
    } else { 
     // Set both scales to the max offset (should be negative, so smaller absolute value) 
     xScale = Math.max(mXOffset, mYOffset); 
     yScale = Math.max(mXOffset, mYOffset); 
    } 

    // Make sure below maximum jump limit 
    if (mMaximumJump > 0) { 
     // Limit x jump 
     if (x - mXTranslation/xScale > mMaximumJump) { 
      x = mXTranslation/xScale + mMaximumJump; 
     } else if (x - mXTranslation/xScale < -mMaximumJump) { 
      x = mXTranslation/xScale - mMaximumJump; 
     } 

     // Limit y jump 
     if (y - mYTranslation/yScale > mMaximumJump) { 
      y = mYTranslation/yScale + mMaximumJump; 
     } else if (y - mYTranslation/yScale < -mMaximumJump) { 
      y = mYTranslation/yScale - mMaximumJump; 
     } 
    } 

    mXTranslation = x * xScale; 
    mYTranslation = y * yScale; 

    configureMatrix(); 
} 

/** 
* Configures the ImageView's imageMatrix to allow for movement of the 
* source image. 
*/ 
private void configureMatrix() { 
    if (getDrawable() == null || getWidth() == 0 || getHeight() == 0) return; 

    int dWidth = getDrawable().getIntrinsicWidth(); 
    int dHeight = getDrawable().getIntrinsicHeight(); 
    int vWidth = getWidth(); 
    int vHeight = getHeight(); 

    float scale; 
    float dx, dy; 

    if (dWidth * vHeight > vWidth * dHeight) { 
     scale = (float) vHeight/(float) dHeight; 
     mXOffset = (vWidth - dWidth * scale * mParallaxIntensity) * 0.5f; 
     mYOffset = (vHeight - dHeight * scale * mParallaxIntensity) * 0.5f; 
    } else { 
     scale = (float) vWidth/(float) dWidth; 
     mXOffset = (vWidth - dWidth * scale * mParallaxIntensity) * 0.5f; 
     mYOffset = (vHeight - dHeight * scale * mParallaxIntensity) * 0.5f; 
    } 

    dx = mXOffset + mXTranslation; 
    dy = mYOffset + mYTranslation; 

    mTranslationMatrix.set(getImageMatrix()); 
    mTranslationMatrix.setScale(mParallaxIntensity * scale, mParallaxIntensity * scale); 
    mTranslationMatrix.postTranslate(dx, dy); 
    setImageMatrix(mTranslationMatrix); 
} 

/** 
* Registers a sensor manager with the parallax ImageView. Should be called in onResume 
* from an Activity or Fragment. 
* 
*/ 
@SuppressWarnings("deprecation") 
public void registerSensorManager() { 
    if (getContext() == null || mSensorManager != null) return; 

    // Acquires a sensor manager 
    mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); 

    if (mSensorManager != null) { 
     mSensorManager.registerListener(this, 
       mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), 
       SensorManager.SENSOR_DELAY_FASTEST); 
    } 
} 

/** 
* Unregisters the ParallaxImageView's SensorManager. Should be called in onPause from 
* an Activity or Fragment to avoid continuing sensor usage. 
*/ 
public void unregisterSensorManager() { 
    unregisterSensorManager(false); 
} 

/** 
* Unregisters the ParallaxImageView's SensorManager. Should be called in onPause from 
* an Activity or Fragment to avoid continuing sensor usage. 
* @param resetTranslation if the image translation should be reset to the origin 
*/ 
public void unregisterSensorManager(boolean resetTranslation) { 
    if (mSensorManager == null) return; 

    mSensorManager.unregisterListener(this); 
    mSensorManager = null; 

    if (resetTranslation) { 
     setTranslate(0, 0); 
    } 
} 

@Override 
public void onSensorChanged(SensorEvent event) { 
    final float [] vectors = mSensorInterpreter.interpretSensorEvent(getContext(), event); 

    // Return if interpretation of data failed 
    if (vectors == null) return; 

    // Set translation on ImageView matrix 
    setTranslate(vectors[2], vectors[1]); 
} 

@Override 
public void onAccuracyChanged(Sensor sensor, int accuracy) { } 

} 

SensorInterpreter

import android.content.Context; 
import android.hardware.SensorEvent; 
import android.view.Surface; 
import android.view.WindowManager; 

/** 
* I did not write this, I just cant remember where I got it from and thought it could be useful for others 
*/ 
public class SensorInterpreter { 

private static final String TAG = SensorInterpreter.class.getName(); 
private float[] mVectors; 
private float mTiltSensitivity = 2.0f; 
private float mForwardTiltOffset = 0.3f; 

public SensorInterpreter() { 
    mVectors = new float[3]; 
} 

public final float[] interpretSensorEvent(Context context, SensorEvent event) { 
    if (event == null || event.values.length < 3 || event.values[0] == 0 
      || event.values[1] == 0 || event.values[2] == 0) 
     return null; 

    // Acquire rotation of screen 
    final int rotation = ((WindowManager) context 
      .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay() 
      .getRotation(); 

    // Adjust for forward tilt based on screen orientation 
    switch (rotation) { 
     case Surface.ROTATION_90: 
      mVectors[0] = event.values[0]; 
      mVectors[1] = event.values[2]; 
      mVectors[2] = -event.values[1]; 
      break; 

     case Surface.ROTATION_180: 
      mVectors[0] = event.values[0]; 
      mVectors[1] = event.values[1]; 
      mVectors[2] = event.values[2]; 
      break; 

     case Surface.ROTATION_270: 
      mVectors[0] = event.values[0]; 
      mVectors[1] = -event.values[2]; 
      mVectors[2] = event.values[1]; 
      break; 

     default: 
      mVectors[0] = event.values[0]; 
      mVectors[1] = -event.values[1]; 
      mVectors[2] = -event.values[2]; 
      break; 
    } 

    // Adjust roll for sensitivity differences based on pitch 
    // double tiltScale = 1/Math.cos(mVectors[1] * Math.PI/180); 
    // if (tiltScale > 12) tiltScale = 12; 
    // if (tiltScale < -12) tiltScale = -12; 
    // mVectors[2] *= tiltScale; 

    // Make roll and pitch percentages out of 1 
    mVectors[1] /= 90; 
    mVectors[2] /= 90; 

    // Add in forward tilt offset 
    mVectors[1] -= mForwardTiltOffset; 
    if (mVectors[1] < -1) 
     mVectors[1] += 2; 

    // Adjust for tilt sensitivity 
    mVectors[1] *= mTiltSensitivity; 
    mVectors[2] *= mTiltSensitivity; 

    // Clamp values to image bounds 
    if (mVectors[1] > 1) 
     mVectors[1] = 1f; 
    if (mVectors[1] < -1) 
     mVectors[1] = -1f; 

    if (mVectors[2] > 1) 
     mVectors[2] = 1f; 
    if (mVectors[2] < -1) 
     mVectors[2] = -1f; 

    return mVectors; 
} 

public float getForwardTiltOffset() { 
    return mForwardTiltOffset; 
} 

public void setForwardTiltOffset(float forwardTiltOffset) { 
    mForwardTiltOffset = forwardTiltOffset; 
} 

public float getTiltSensitivity() { 
    return mTiltSensitivity; 
} 

public void setTiltSensitivity(float tiltSensitivity) { 
    mTiltSensitivity = tiltSensitivity; 
} 

これは

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
<declare-styleable name="ParallaxImageView"> 
    <attr name="intensity" format="float" /> 
    <attr name="tiltSensitivity" format="float" /> 
    <attr name="forwardTiltOffset" format="float" /> 
    <attr name="scaledIntensity" format="boolean" /> 
</declare-styleable> 

は、レイアウトXML

<yourpackagenamehere.ParallaxImageView 
    android:id="@+id/background" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:scaleType="centerCrop" /> 
0にこれを追加attrs.xmlに追加

private ParallaxImageView background; 


background = (ParallaxImageView) findViewById(R.id.background); 
background.setImageResource(R.drawable.main_back); 


@Override 
public void onResume() { 
    background.registerSensorManager(); 
    super.onResume(); 
} 

@Override 
public void onPause() { 
    background.unregisterSensorManager(); 
    super.onPause(); 
}