diff --git a/README.md b/README.md
index ceafd25..8b8db7e 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,10 @@ VideoListPlayer实现了在列表控件(ListView, RecyclerView)中加载并
#Changelogs
+**v.14**
+ 1.支持更多类型的scaleType,详见 [Android-ScalableVideoView](https://github.com/yqritc/Android-ScalableVideoView)
+ 2.加入 `getCurrentPosition()` 和 `getDuration()` 接口
+
**v1.3**
fix在多类型列表元素中出现视频无法正常播放的bug
diff --git a/app/src/main/java/com/waynell/videolist/demo/holder/VideoViewHolder.java b/app/src/main/java/com/waynell/videolist/demo/holder/VideoViewHolder.java
index aeb9fff..3bf287e 100644
--- a/app/src/main/java/com/waynell/videolist/demo/holder/VideoViewHolder.java
+++ b/app/src/main/java/com/waynell/videolist/demo/holder/VideoViewHolder.java
@@ -82,6 +82,8 @@ void cliclVideoView() {
videoView.mute();
Toast.makeText(itemView.getContext(), "turn off video sound", Toast.LENGTH_SHORT).show();
}
+
+ Toast.makeText(itemView.getContext(), "durration: " + videoView.getDuration() + " pos: " + videoView.getCurrentPosition(), Toast.LENGTH_LONG).show();
}
private void reset() {
diff --git a/app/src/main/res/layout/video_list_item.xml b/app/src/main/res/layout/video_list_item.xml
index 9d5b0a8..f187a6b 100644
--- a/app/src/main/res/layout/video_list_item.xml
+++ b/app/src/main/res/layout/video_list_item.xml
@@ -4,6 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="300dp"
+ android:background="#000000"
android:orientation="vertical">
+ android:layout_height="300dp"
+ app:scaleType="fitCenter"/>
viewHeight) { // device in landscape
- scaleX = (viewHeight * contentWidth) / (viewWidth * contentHeight);
- } else {
- scaleY = (viewWidth * contentHeight) / (viewHeight * contentWidth);
- }
- break;
- case BOTTOM:
- case CENTER_CROP:
- case TOP:
- if (contentWidth > viewWidth && contentHeight > viewHeight) {
- scaleX = contentWidth / viewWidth;
- scaleY = contentHeight / viewHeight;
- } else if (contentWidth < viewWidth && contentHeight < viewHeight) {
- scaleY = viewWidth / contentWidth;
- scaleX = viewHeight / contentHeight;
- } else if (viewWidth > contentWidth) {
- scaleY = (viewWidth / contentWidth) / (viewHeight / contentHeight);
- } else if (viewHeight > contentHeight) {
- scaleX = (viewHeight / contentHeight) / (viewWidth / contentWidth);
- }
- break;
- }
-
- // Calculate pivot points, in our case crop from center
- float pivotPointX;
- float pivotPointY;
-
- switch (mScaleType) {
- case TOP:
- pivotPointX = 0;
- pivotPointY = 0;
- break;
- case BOTTOM:
- pivotPointX = viewWidth;
- pivotPointY = viewHeight;
- break;
- case CENTER_CROP:
- pivotPointX = viewWidth / 2;
- pivotPointY = viewHeight / 2;
- break;
- case FILL:
- pivotPointX = mPivotPointX;
- pivotPointY = mPivotPointY;
- break;
- default:
- throw new IllegalStateException("pivotPointX, pivotPointY for ScaleType " + mScaleType + " are not defined");
- }
-
- float fitCoef = 1;
- switch (mScaleType) {
- case FILL:
- break;
- case BOTTOM:
- case CENTER_CROP:
- case TOP:
- if (mContentHeight > mContentWidth) { //Portrait video
- fitCoef = viewWidth / (viewWidth * scaleX);
- } else { //Landscape video
- fitCoef = viewHeight / (viewHeight * scaleY);
- }
- break;
- }
-
- mContentScaleX = fitCoef * scaleX;
- mContentScaleY = fitCoef * scaleY;
-
- mPivotPointX = pivotPointX;
- mPivotPointY = pivotPointY;
-
- updateMatrixScaleRotate();
- }
-
- private void updateMatrixScaleRotate() {
- mTransformMatrix.reset();
- mTransformMatrix.setScale(mContentScaleX * mContentScaleMultiplier, mContentScaleY * mContentScaleMultiplier, mPivotPointX, mPivotPointY);
- mTransformMatrix.postRotate(mContentRotation, mPivotPointX, mPivotPointY);
- setTransform(mTransformMatrix);
- }
-
- private void updateMatrixTranslate() {
- float scaleX = mContentScaleX * mContentScaleMultiplier;
- float scaleY = mContentScaleY * mContentScaleMultiplier;
-
- mTransformMatrix.reset();
- mTransformMatrix.setScale(scaleX, scaleY, mPivotPointX, mPivotPointY);
- mTransformMatrix.postTranslate(mContentX, mContentY);
- setTransform(mTransformMatrix);
- }
-
- @Override
- public void setRotation(float degrees) {
- mContentRotation = degrees;
-
- updateMatrixScaleRotate();
- }
-
- @Override
- public float getRotation() {
- return mContentRotation;
- }
-
- @Override
- public void setPivotX(float pivotX) {
- mPivotPointX = pivotX;
- }
-
- @Override
- public void setPivotY(float pivotY) {
- mPivotPointY = pivotY;
- }
-
- @Override
- public float getPivotX() {
- return mPivotPointX;
- }
-
- @Override
- public float getPivotY() {
- return mPivotPointY;
- }
-
- public float getContentAspectRatio() {
- return mContentWidth != null && mContentHeight != null
- ? (float) mContentWidth / (float) mContentHeight
- : 0;
- }
-
- /**
- * Use it to animate TextureView content x position
- * @param x
- */
- public final void setContentX(float x) {
- mContentX = (int) x - (getMeasuredWidth() - getScaledContentWidth()) / 2;
- updateMatrixTranslate();
- }
-
- /**
- * Use it to animate TextureView content x position
- * @param y
- */
- public final void setContentY(float y) {
- mContentY = (int) y - (getMeasuredHeight() - getScaledContentHeight()) / 2;
- updateMatrixTranslate();
- }
-
- protected final float getContentX() {
- return mContentX;
- }
-
- protected final float getContentY() {
- return mContentY;
- }
-
- /**
- * Use it to set content of a TextureView in the center of TextureView
- */
- public void centralizeContent() {
- int measuredWidth = getMeasuredWidth();
- int measuredHeight = getMeasuredHeight();
- int scaledContentWidth = getScaledContentWidth();
- int scaledContentHeight = getScaledContentHeight();
-
- mContentX = 0;
- mContentY = 0;
-
- updateMatrixScaleRotate();
- }
-
- public Integer getScaledContentWidth() {
- return (int) (mContentScaleX * mContentScaleMultiplier * getMeasuredWidth());
- }
-
- public Integer getScaledContentHeight() {
- return (int) (mContentScaleY * mContentScaleMultiplier * getMeasuredHeight());
- }
-
- public float getContentScale() {
- return mContentScaleMultiplier;
- }
-
- public void setContentScale(float contentScale) {
- mContentScaleMultiplier = contentScale;
- updateMatrixScaleRotate();
- }
-
- protected final void setContentHeight(int height) {
- mContentHeight = height;
- }
-
- protected final Integer getContentHeight() {
- return mContentHeight;
- }
-
- protected final void setContentWidth(int width) {
- mContentWidth = width;
- }
-
- protected final Integer getContentWidth() {
- return mContentWidth;
- }
-
-}
diff --git a/video-list-player/src/main/java/com/waynell/videolist/widget/ScaleManager.java b/video-list-player/src/main/java/com/waynell/videolist/widget/ScaleManager.java
new file mode 100644
index 0000000..c665a4f
--- /dev/null
+++ b/video-list-player/src/main/java/com/waynell/videolist/widget/ScaleManager.java
@@ -0,0 +1,192 @@
+package com.waynell.videolist.widget;
+
+import android.graphics.Matrix;
+
+/**
+ * Create On 05/01/2017
+ *
+ * @author Wayne
+ */
+class ScaleManager {
+ private Size mViewSize;
+ private Size mVideoSize;
+
+ ScaleManager(Size viewSize, Size videoSize) {
+ mViewSize = viewSize;
+ mVideoSize = videoSize;
+ }
+
+ Matrix getScaleMatrix(ScaleType scaleType) {
+ switch (scaleType) {
+ case NONE:
+ return getNoScale();
+
+ case FIT_XY:
+ return fitXY();
+ case FIT_CENTER:
+ return fitCenter();
+ case FIT_START:
+ return fitStart();
+ case FIT_END:
+ return fitEnd();
+
+ case LEFT_TOP:
+ return getOriginalScale(PivotPoint.LEFT_TOP);
+ case LEFT_CENTER:
+ return getOriginalScale(PivotPoint.LEFT_CENTER);
+ case LEFT_BOTTOM:
+ return getOriginalScale(PivotPoint.LEFT_BOTTOM);
+ case CENTER_TOP:
+ return getOriginalScale(PivotPoint.CENTER_TOP);
+ case CENTER:
+ return getOriginalScale(PivotPoint.CENTER);
+ case CENTER_BOTTOM:
+ return getOriginalScale(PivotPoint.CENTER_BOTTOM);
+ case RIGHT_TOP:
+ return getOriginalScale(PivotPoint.RIGHT_TOP);
+ case RIGHT_CENTER:
+ return getOriginalScale(PivotPoint.RIGHT_CENTER);
+ case RIGHT_BOTTOM:
+ return getOriginalScale(PivotPoint.RIGHT_BOTTOM);
+
+ case LEFT_TOP_CROP:
+ return getCropScale(PivotPoint.LEFT_TOP);
+ case LEFT_CENTER_CROP:
+ return getCropScale(PivotPoint.LEFT_CENTER);
+ case LEFT_BOTTOM_CROP:
+ return getCropScale(PivotPoint.LEFT_BOTTOM);
+ case CENTER_TOP_CROP:
+ return getCropScale(PivotPoint.CENTER_TOP);
+ case CENTER_CROP:
+ return getCropScale(PivotPoint.CENTER);
+ case CENTER_BOTTOM_CROP:
+ return getCropScale(PivotPoint.CENTER_BOTTOM);
+ case RIGHT_TOP_CROP:
+ return getCropScale(PivotPoint.RIGHT_TOP);
+ case RIGHT_CENTER_CROP:
+ return getCropScale(PivotPoint.RIGHT_CENTER);
+ case RIGHT_BOTTOM_CROP:
+ return getCropScale(PivotPoint.RIGHT_BOTTOM);
+
+ case START_INSIDE:
+ return startInside();
+ case CENTER_INSIDE:
+ return centerInside();
+ case END_INSIDE:
+ return endInside();
+
+ default:
+ return null;
+ }
+ }
+
+ private Matrix getMatrix(float sx, float sy, float px, float py) {
+ Matrix matrix = new Matrix();
+ matrix.setScale(sx, sy, px, py);
+ return matrix;
+ }
+
+ private Matrix getMatrix(float sx, float sy, PivotPoint pivotPoint) {
+ switch (pivotPoint) {
+ case LEFT_TOP:
+ return getMatrix(sx, sy, 0, 0);
+ case LEFT_CENTER:
+ return getMatrix(sx, sy, 0, mViewSize.getHeight() / 2f);
+ case LEFT_BOTTOM:
+ return getMatrix(sx, sy, 0, mViewSize.getHeight());
+ case CENTER_TOP:
+ return getMatrix(sx, sy, mViewSize.getWidth() / 2f, 0);
+ case CENTER:
+ return getMatrix(sx, sy, mViewSize.getWidth() / 2f, mViewSize.getHeight() / 2f);
+ case CENTER_BOTTOM:
+ return getMatrix(sx, sy, mViewSize.getWidth() / 2f, mViewSize.getHeight());
+ case RIGHT_TOP:
+ return getMatrix(sx, sy, mViewSize.getWidth(), 0);
+ case RIGHT_CENTER:
+ return getMatrix(sx, sy, mViewSize.getWidth(), mViewSize.getHeight() / 2f);
+ case RIGHT_BOTTOM:
+ return getMatrix(sx, sy, mViewSize.getWidth(), mViewSize.getHeight());
+ default:
+ throw new IllegalArgumentException("Illegal PivotPoint");
+ }
+ }
+
+ private Matrix getNoScale() {
+ float sx = mVideoSize.getWidth() / (float) mViewSize.getWidth();
+ float sy = mVideoSize.getHeight() / (float) mViewSize.getHeight();
+ return getMatrix(sx, sy, PivotPoint.LEFT_TOP);
+ }
+
+ private Matrix getFitScale(PivotPoint pivotPoint) {
+ float sx = (float) mViewSize.getWidth() / mVideoSize.getWidth();
+ float sy = (float) mViewSize.getHeight() / mVideoSize.getHeight();
+ float minScale = Math.min(sx, sy);
+ sx = minScale / sx;
+ sy = minScale / sy;
+ return getMatrix(sx, sy, pivotPoint);
+ }
+
+ private Matrix fitXY() {
+ return getMatrix(1, 1, PivotPoint.LEFT_TOP);
+ }
+
+ private Matrix fitStart() {
+ return getFitScale(PivotPoint.LEFT_TOP);
+ }
+
+ private Matrix fitCenter() {
+ return getFitScale(PivotPoint.CENTER);
+ }
+
+ private Matrix fitEnd() {
+ return getFitScale(PivotPoint.RIGHT_BOTTOM);
+ }
+
+ private Matrix getOriginalScale(PivotPoint pivotPoint) {
+ float sx = mVideoSize.getWidth() / (float) mViewSize.getWidth();
+ float sy = mVideoSize.getHeight() / (float) mViewSize.getHeight();
+ return getMatrix(sx, sy, pivotPoint);
+ }
+
+ private Matrix getCropScale(PivotPoint pivotPoint) {
+ float sx = (float) mViewSize.getWidth() / mVideoSize.getWidth();
+ float sy = (float) mViewSize.getHeight() / mVideoSize.getHeight();
+ float maxScale = Math.max(sx, sy);
+ sx = maxScale / sx;
+ sy = maxScale / sy;
+ return getMatrix(sx, sy, pivotPoint);
+ }
+
+ private Matrix startInside() {
+ if (mVideoSize.getHeight() <= mViewSize.getWidth()
+ && mVideoSize.getHeight() <= mViewSize.getHeight()) {
+ // video is smaller than view size
+ return getOriginalScale(PivotPoint.LEFT_TOP);
+ } else {
+ // either of width or height of the video is larger than view size
+ return fitStart();
+ }
+ }
+
+ private Matrix centerInside() {
+ if (mVideoSize.getHeight() <= mViewSize.getWidth()
+ && mVideoSize.getHeight() <= mViewSize.getHeight()) {
+ // video is smaller than view size
+ return getOriginalScale(PivotPoint.CENTER);
+ } else {
+ // either of width or height of the video is larger than view size
+ return fitCenter();
+ }
+ }
+
+ private Matrix endInside() {
+ if (mVideoSize.getHeight() <= mViewSize.getWidth()
+ && mVideoSize.getHeight() <= mViewSize.getHeight()) {
+ // video is smaller than view size
+ return getOriginalScale(PivotPoint.RIGHT_BOTTOM);
+ } else {
+ // either of width or height of the video is larger than view size
+ return fitEnd();
+ }
+ }
+}
diff --git a/video-list-player/src/main/java/com/waynell/videolist/widget/ScaleType.java b/video-list-player/src/main/java/com/waynell/videolist/widget/ScaleType.java
new file mode 100644
index 0000000..65c0ca2
--- /dev/null
+++ b/video-list-player/src/main/java/com/waynell/videolist/widget/ScaleType.java
@@ -0,0 +1,38 @@
+package com.waynell.videolist.widget;
+
+/**
+ * Create On 05/01/2017
+ * @author Wayne
+ */
+public enum ScaleType {
+ NONE,
+
+ FIT_XY,
+ FIT_START,
+ FIT_CENTER,
+ FIT_END,
+
+ LEFT_TOP,
+ LEFT_CENTER,
+ LEFT_BOTTOM,
+ CENTER_TOP,
+ CENTER,
+ CENTER_BOTTOM,
+ RIGHT_TOP,
+ RIGHT_CENTER,
+ RIGHT_BOTTOM,
+
+ LEFT_TOP_CROP,
+ LEFT_CENTER_CROP,
+ LEFT_BOTTOM_CROP,
+ CENTER_TOP_CROP,
+ CENTER_CROP,
+ CENTER_BOTTOM_CROP,
+ RIGHT_TOP_CROP,
+ RIGHT_CENTER_CROP,
+ RIGHT_BOTTOM_CROP,
+
+ START_INSIDE,
+ CENTER_INSIDE,
+ END_INSIDE
+}
diff --git a/video-list-player/src/main/java/com/waynell/videolist/widget/Size.java b/video-list-player/src/main/java/com/waynell/videolist/widget/Size.java
new file mode 100644
index 0000000..2de7c3a
--- /dev/null
+++ b/video-list-player/src/main/java/com/waynell/videolist/widget/Size.java
@@ -0,0 +1,23 @@
+package com.waynell.videolist.widget;
+
+/**
+ * Create On 05/01/2017
+ * @author Wayne
+ */
+public class Size {
+ private int mWidth;
+ private int mHeight;
+
+ public Size(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+}
diff --git a/video-list-player/src/main/java/com/waynell/videolist/widget/TextureVideoView.java b/video-list-player/src/main/java/com/waynell/videolist/widget/TextureVideoView.java
index 166faaa..44f74db 100644
--- a/video-list-player/src/main/java/com/waynell/videolist/widget/TextureVideoView.java
+++ b/video-list-player/src/main/java/com/waynell/videolist/widget/TextureVideoView.java
@@ -3,6 +3,8 @@
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaExtractor;
@@ -12,6 +14,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
@@ -19,6 +22,7 @@
import android.view.TextureView;
import com.waynell.videolist.BuildConfig;
+import com.waynell.videolist.R;
import java.io.IOException;
@@ -29,7 +33,7 @@
* @author Wayne
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
-public class TextureVideoView extends ScalableTextureView
+public class TextureVideoView extends TextureView
implements TextureView.SurfaceTextureListener,
Handler.Callback,
MediaPlayer.OnPreparedListener,
@@ -70,6 +74,8 @@ public class TextureVideoView extends ScalableTextureView
private boolean mSoundMute;
private boolean mHasAudio;
+ private ScaleType mScaleType = ScaleType.NONE;
+
private static final HandlerThread sThread = new HandlerThread("VideoPlayThread");
static {
sThread.start();
@@ -86,17 +92,27 @@ public interface MediaPlayerCallback {
}
public TextureVideoView(Context context) {
- super(context);
- init();
+ this(context, null);
}
public TextureVideoView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
+ this(context, attrs, 0);
}
public TextureVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ if (attrs == null) {
+ return;
+ }
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.scaleStyle, 0, 0);
+ if (a == null) {
+ return;
+ }
+
+ int scaleType = a.getInt(R.styleable.scaleStyle_scaleType, ScaleType.NONE.ordinal());
+ a.recycle();
+ mScaleType = ScaleType.values()[scaleType];
init();
}
@@ -107,6 +123,70 @@ public void setMediaPlayerCallback(MediaPlayerCallback mediaPlayerCallback) {
}
}
+ public int getCurrentPosition() {
+ if (isInPlaybackState()) {
+ return mMediaPlayer.getCurrentPosition();
+ }
+ return 0;
+ }
+
+ public int getDuration() {
+ if (isInPlaybackState()) {
+ return mMediaPlayer.getDuration();
+ }
+
+ return -1;
+ }
+
+ public int getVideoHeight() {
+ if (mMediaPlayer != null) {
+ return mMediaPlayer.getVideoHeight();
+ }
+ return 0;
+ }
+
+ public int getVideoWidth() {
+ if (mMediaPlayer != null) {
+ return mMediaPlayer.getVideoWidth();
+ }
+ return 0;
+ }
+
+ public void setScaleType(ScaleType scaleType) {
+ mScaleType = scaleType;
+ scaleVideoSize(getVideoWidth(), getVideoHeight());
+ }
+
+ public ScaleType getScaleType() {
+ return mScaleType;
+ }
+
+ private void scaleVideoSize(int videoWidth, int videoHeight) {
+ if (videoWidth == 0 || videoHeight == 0) {
+ return;
+ }
+
+ Size viewSize = new Size(getWidth(), getHeight());
+ Size videoSize = new Size(videoWidth, videoHeight);
+ ScaleManager scaleManager = new ScaleManager(viewSize, videoSize);
+ final Matrix matrix = scaleManager.getScaleMatrix(mScaleType);
+ if (matrix == null) {
+ return;
+ }
+
+ if(Looper.myLooper() == Looper.getMainLooper()) {
+ setTransform(matrix);
+ }
+ else {
+ mHandler.postAtFrontOfQueue(new Runnable() {
+ @Override
+ public void run() {
+ setTransform(matrix);
+ }
+ });
+ }
+ }
+
@Override
public boolean handleMessage(Message msg) {
synchronized (TextureVideoView.class) {
@@ -140,12 +220,14 @@ public boolean handleMessage(Message msg) {
}
private void init() {
- mContext = getContext();
- mCurrentState = STATE_IDLE;
- mTargetState = STATE_IDLE;
- mHandler = new Handler();
- mVideoHandler = new Handler(sThread.getLooper(), this);
- setSurfaceTextureListener(this);
+ if(!isInEditMode()) {
+ mContext = getContext();
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
+ mHandler = new Handler();
+ mVideoHandler = new Handler(sThread.getLooper(), this);
+ setSurfaceTextureListener(this);
+ }
}
@@ -215,21 +297,7 @@ private void openVideo() {
}
}
- } catch (IOException ex) {
- if(SHOW_LOGS) Log.w(TAG, "Unable to open content: " + mUri, ex);
- mCurrentState = STATE_ERROR;
- mTargetState = STATE_ERROR;
- if (mMediaPlayerCallback != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if(mMediaPlayerCallback != null) {
- mMediaPlayerCallback.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
- }
- }
- });
- }
- } catch (IllegalArgumentException ex) {
+ } catch (IOException | IllegalArgumentException ex) {
if(SHOW_LOGS) Log.w(TAG, "Unable to open content: " + mUri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
@@ -417,6 +485,7 @@ public void run() {
@Override
public void onVideoSizeChanged(final MediaPlayer mp, final int width, final int height) {
+ scaleVideoSize(width, height);
if (mMediaPlayerCallback != null) {
mHandler.post(new Runnable() {
@Override
diff --git a/video-list-player/src/main/res/values/attrs.xml b/video-list-player/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..c2cee7f
--- /dev/null
+++ b/video-list-player/src/main/res/values/attrs.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file