SurfaceView绘图、调用Camera

article/2025/11/3 7:28:44
一、使用surfaceview绘图
 * <p>
 *   Android系统提供了View进行绘图处理,我们通过自定义的View可以满足大部分的绘图需求,但是这有个问题就
 *   是我们通常自定义的View是用于主动更新情况的,用户无法控制其绘制的速度,由于View是通过invalidate方法
 *   通知系统去调用view.onDraw方法进行重绘,而Android系统是通过发出VSYNC信号来进行屏幕的重绘,刷新的时间
 *   是16ms,如果在16ms内View完成不了执行的操作,用户就会看着卡顿,比如当draw方法里执行的逻辑过多,需要频
 *   繁刷新的界面上,例如游戏界面,那么就会不断的阻塞主线程,从而导致画面卡顿。而SurfaceView相当于是另一
 *   个绘图线程,它是不会阻碍主线程,并且它在底层实现机制中实现了双缓冲机制。
 * </p>
 *
 * <p>
 *   1、获取surfaceview 实例对象
 *   2、添加SurfaceHolderCallback回调
 *   3、在surfaceCreated回调方法中调用绘图逻辑
 * </p>
 */
public class SurfaceViewActivity extends AppCompatActivity  implements View.OnTouchListener {private SurfaceView    mSurfaceView;
     private SurfaceHolder  mSurfaceHolder;
     private boolean        isSurfaceCreated;
     private SurfaceViewActivity  mActivity;
     private int             width,height;
     private Canvas           canvas;
     private Paint            mPaint;
    // 路径
     private Path mPath;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_surface_view);
        mSurfaceView=findViewById(R.id.surface_sv);
        mActivity=this;
        mSurfaceHolder=mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(new MySurfaceHolderCallback());
        mSurfaceView.setFocusable(true);
        mSurfaceView.setFocusableInTouchMode(true);
        mSurfaceView.setKeepScreenOn(true);
        initCanvasAndPaint();

    }@Override
    public boolean onTouch(View v, MotionEvent event) {return false;
    }//添加surfaceview的holder回调
    private class MySurfaceHolderCallback  implements  SurfaceHolder.Callback{@Override
        public void surfaceCreated(SurfaceHolder holder) {isSurfaceCreated = true;
            width = mSurfaceView.getWidth();
            height = mSurfaceView.getHeight();
            //开启子线程绘图
            new MyThread().start();
        }@Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Override
        public void surfaceDestroyed(SurfaceHolder holder) {isSurfaceCreated=false;
        }}private class  MyThread extends  Thread{@Override
        public void run() {super.run();
                 while (isSurfaceCreated){drawing();
                 }}}private void initCanvasAndPaint(){mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
      mPaint.setStrokeWidth(3);
      mPaint.setStyle(Paint.Style.FILL); /**Paint.Style.STROKE*/
      mPaint.setStrokeJoin(Paint.Join.ROUND);
      mPaint.setStrokeCap(Paint.Cap.ROUND);
      mPath = new Path();
  }private void drawing(){float x= (float) (width/2.0);
         float y= (float) (height/2.0);
         try {//获取画布
             canvas=mSurfaceHolder.lockCanvas();

             // 画圆
             mPaint.setColor(ContextCompat.getColor(mActivity,android.R.color.holo_blue_dark));
             canvas.drawCircle(x,y,y,mPaint);

             //贝赛尔曲线
             mPaint.setColor(ContextCompat.getColor(mActivity,android.R.color.holo_red_dark));
             mPath.moveTo(x,y-40);
             mPath.quadTo(x-100,y-120,x,y+80);
             mPath.close();
             canvas.drawPath(mPath,mPaint);

             mPath.moveTo(x,y-40);
             mPath.quadTo(x+100,y-120,x,y+80);
             mPath.close();
             canvas.drawPath(mPath,mPaint);


             //绘制箭头
             mPaint.setColor(ContextCompat.getColor(mActivity,android.R.color.holo_green_dark));
             canvas.drawLine(0,y,x+y,y,mPaint);
             mPath.moveTo(x+y,y-40);
             mPath.lineTo(x+y,y+40);
             mPath.lineTo(x+y+60,y);
             mPath.close();
             canvas.drawPath(mPath,mPaint);

             //异步更新画布
             mSurfaceHolder.unlockCanvasAndPost(canvas);
         }catch (Exception e){if (canvas!=null)mSurfaceHolder.unlockCanvasAndPost(canvas);
         }}
}
 

二、调用Camera

 1、自定义属性

<declare-styleable name="CustomCamera">
    <attr name="custom_camera_id" format="integer" >
        <enum name="back" value="99" />
        <enum name="front" value="98" />
    </attr>
</declare-styleable>

  在xml中引用配置前置后置摄像头

2、自定义相机的java类

public class CustomCamera extends SurfaceView  implements SurfaceHolder.Callback{private String TAG="CustomCamera";
    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    public static  final int CAMERA_ID[]={98,99};
    private int mWith,mHeight;//
    private CameraCaptureSession mCameraCaptureSession;
    private CameraDevice mCameraDevice;
    private int         mCameraId=CAMERA_ID[1];  //98前置摄像头 99后置摄像头
    private ImageReader mImageReader;
    private Handler mWorkHandler, mUiHandler;
    private  SurfaceHolder mSurfaceHolder;
    private CaptureRequest.Builder previewRequestBuilder;
    private Activity   mContext;

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)public CustomCamera(Context context,int cameraId) {super(context);
        this.mContext= (Activity) context;
        this.mCameraId=cameraId;
        init();
    }@RequiresApi(api = Build.VERSION_CODES.KITKAT)public CustomCamera(Context context, AttributeSet attrs) {super(context, attrs);
        this.mContext= (Activity) context;
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomCamera);
        mCameraId=typedArray.getInt(R.styleable.CustomCamera_custom_camera_id,99);
        typedArray.recycle();
        init();
    }@RequiresApi(api = Build.VERSION_CODES.KITKAT)public CustomCamera(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        this.mContext= (Activity) context;
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomCamera);
        mCameraId=typedArray.getInt(R.styleable.CustomCamera_custom_camera_id,99);
        typedArray.recycle();
        init();
    }static {ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWith=MeasureSpec.getSize(widthMeasureSpec);
        mHeight=MeasureSpec.getSize(heightMeasureSpec);
        Log.i(TAG,"mWith="+mWith+"---------------mHeight="+mHeight);
    }@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Override
    public void surfaceCreated(SurfaceHolder holder) {initCamera2();
    }@Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Override
    public void surfaceDestroyed(SurfaceHolder holder) {if (null != mCameraDevice) {mCameraDevice.close();
            this.mCameraDevice = null;
        }}@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void  init(){mSurfaceHolder= this.getHolder();
        mSurfaceHolder.setKeepScreenOn(true);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mSurfaceHolder.addCallback(this);
        HandlerThread mHandlerThread = new HandlerThread("Camera2");
        mHandlerThread.start();
        mUiHandler = new Handler(mContext.getMainLooper());
        mWorkHandler = new Handler(mHandlerThread.getLooper());
    }/**
     * 初始化Camera2
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)private void initCamera2() {try {if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {Toast.makeText(mContext,"摄像头未开启",Toast.LENGTH_LONG).show();
            } else {String mCameraID =mCameraId==99?String.valueOf(CameraCharacteristics.LENS_FACING_FRONT):String.valueOf(CameraCharacteristics.LENS_FACING_BACK);
                CameraManager mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
                assert mCameraManager != null;
                mCameraManager.openCamera(mCameraID, stateCallback, mUiHandler);
            }} catch (CameraAccessException e) {e.printStackTrace();
        }}/**
     * 摄像头创建监听
     */
    private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Override
        public void onOpened(@NonNull CameraDevice camera) {try {//打开摄像头 配置自动对焦、打开闪光灯、打开闪光灯
                mCameraDevice = camera;
                previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                previewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
                mImageReader = ImageReader.newInstance(mWith, mHeight, ImageFormat.JPEG, 1);
                mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(),
                        mImageReader.getSurface()),callback,mWorkHandler);
            } catch (CameraAccessException e) {e.printStackTrace();
            }}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Override
        public void onDisconnected(@NonNull CameraDevice camera) {//关闭摄像头
            if (null != mCameraDevice) {mCameraDevice.close();
                mCameraDevice = null;
            }}@Override
        public void onError(@NonNull CameraDevice camera, int error) {Toast.makeText(getContext(), "开启摄像头失败", Toast.LENGTH_SHORT).show();
        }};

    /**
     *  配置摄像头,预览回调
     */
    private CameraCaptureSession.StateCallback  callback=new CameraCaptureSession.StateCallback() {@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {try {mCameraCaptureSession = cameraCaptureSession;
                mCameraCaptureSession.setRepeatingRequest(previewRequestBuilder.build(), null, mWorkHandler);
            } catch (CameraAccessException e) {e.printStackTrace();
            }}@Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {Toast.makeText(getContext(), "配置失败", Toast.LENGTH_SHORT).show();
        }};

    /**
     *  回调处理拿到拍照照片数据
     */
   private ImageReader.OnImageAvailableListener  mImageAvailableListener=new ImageReader.OnImageAvailableListener() {@RequiresApi(api = Build.VERSION_CODES.KITKAT)@Override
    public void onImageAvailable(ImageReader reader) {Image image = reader.acquireNextImage();
         ByteBuffer buffer = image.getPlanes()[0].getBuffer();
         byte[] bytes = new byte[buffer.remaining()];
         buffer.get(bytes);
         Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        if (bitmap != null&&showPhotoListener!=null)showPhotoListener.show(bitmap);
    }
};

    /**
     * 调用拍照,执行回调将拍到的照片显示到指定的控件上
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public void takePhoto() {try {previewRequestBuilder.addTarget(mImageReader.getSurface());
            // 获取手机横屏竖屏方向,根据设备方向计算设置照片的方向
            previewRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(mContext.getWindowManager().getDefaultDisplay().getRotation()));
            mCameraCaptureSession.capture(previewRequestBuilder.build(), null, mWorkHandler);

            mImageReader.setOnImageAvailableListener(mImageAvailableListener, mUiHandler);

        } catch (CameraAccessException e) {e.printStackTrace();
        }}/**
     * 暴漏接口,回调处理拍照结果
     */
    public interface  ShowPhotoListener{void show(Bitmap  bitmap);
    }public ShowPhotoListener showPhotoListener;

    public void setShowPhotoListener(ShowPhotoListener showPhotoListener) {this.showPhotoListener = showPhotoListener;
    }public static int getScreenWidth(Context context) {WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        assert manager != null;
        manager.getDefaultDisplay().getMetrics(metrics);
        return metrics.widthPixels;
    }public static int getScreenHeight(Context context) {WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        assert manager != null;
        manager.getDefaultDisplay().getMetrics(metrics);
        return metrics.heightPixels;
    }}
 

3、调用方法

  

public class TakePhotoActivity extends AppCompatActivity {private LinearLayout mParent;
    private CustomCamera customCamera;
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)@Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_take_photo);
        mParent=findViewById(R.id.parent_ll);
        customCamera=new CustomCamera(this,99);
        LinearLayout.LayoutParams  lp1= new LinearLayout.LayoutParams(CustomCamera.getScreenWidth(this),CustomCamera.getScreenHeight(this)/2);
        customCamera.setLayoutParams(lp1);
        mParent.addView(customCamera);

        final ImageView  imageView=new ImageView(this);
         mParent.addView(imageView);
        LinearLayout.LayoutParams  lp= (LinearLayout.LayoutParams) imageView.getLayoutParams();
        lp.width=CustomCamera.getScreenWidth(this)/2;
        lp.width=CustomCamera.getScreenHeight(this)/4;
        lp.topMargin=20;
        imageView.setLayoutParams(lp);

        TextView  textView=new TextView(this);
        textView.setText("拍照");
        textView.setTextSize(20);
        mParent.addView(textView);

        textView.setOnClickListener(new View.OnClickListener() {@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Override
            public void onClick(View v) {customCamera.takePhoto();
            }});

        customCamera.setShowPhotoListener(new CustomCamera.ShowPhotoListener() {@Override
            public void show(Bitmap bitmap) {imageView.setImageBitmap(bitmap);
                if (bitmap.isRecycled())bitmap.recycle();
            }});

    }
}
 
 

三、使用SurfaceView播放网络视频

public class CustomVideoPlayer extends FrameLayout implements SurfaceHolder.Callback, View.OnClickListener {private String TAG = getClass().getSimpleName();

    private Context mContext;

    private int mWith, mHeight;

    @SuppressLint("StaticFieldLeak")protected static SeekBar mSeekBar;

    private Button mPlayerBtn;

    private SurfaceHolder mSurfaceHolder;

    @SuppressLint("StaticFieldLeak")private static MediaPlayer mMediaPlayer;

    private String mPath;  //視頻地址

    private static boolean isPause = true;//暂停

    private ExecutorService mExecutorService;

    private static int curSeekPosition = 0;//进度条进度

    private static int mSeekBarWidth;

    private SeekHandler mSeekHandler;

    private static int PROGRESS_UPDATE = 1;

    private static int PROGRESS_MAX = 100;

    private static int mTotalTime;

    private static final SparseIntArray orientations = new SparseIntArray();//手机旋转对应的调整角度

    static {orientations.append(Surface.ROTATION_0, 90);
        orientations.append(Surface.ROTATION_90, 0);
        orientations.append(Surface.ROTATION_180, 270);
        orientations.append(Surface.ROTATION_270, 180);
    }public CustomVideoPlayer(Context context, String mPath) {super(context);
        mContext = context;
        this.mPath = mPath;
        init();
    }public CustomVideoPlayer(Context context, AttributeSet attrs) {super(context, attrs);
        mContext = context;
        init();
    }public CustomVideoPlayer(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        mContext = context;
        init();
    }@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWith = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
    }@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);

        int margin = dp2px(mContext, 10);
        int wh = dp2px(mContext, 30);
        int marginTop = dp2px(mContext, 50);

        for (int i = 0, len = getChildCount(); i < len; i++) {View view = getChildAt(i);
            if (view instanceof SurfaceView) {view.layout(0, 0, mWith, mHeight - marginTop);
            } else if (view instanceof RelativeLayout) {RelativeLayout rel = (RelativeLayout) view;
                view.setBackgroundColor(Color.parseColor("#3385ff"));
                rel.layout(0, mHeight - marginTop, mWith, mHeight);
                for (int j = 0, cLen = rel.getChildCount(); j < cLen; j++) {View childView = rel.getChildAt(j);
                    if (childView instanceof SeekBar) {childView.layout(2 * margin, marginTop - wh, mWith - margin, wh);
                        mSeekBarWidth = mWith - 2 * margin - wh;
                    } else if (childView instanceof ImageView) {childView.layout(margin, margin, margin + wh, margin + wh);
                    }}}}}@Override
    public void surfaceCreated(SurfaceHolder holder) {initPlayer();
    }@Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Override
    public void surfaceDestroyed(SurfaceHolder holder) {if (mMediaPlayer != null && mMediaPlayer.isPlaying())stop();
    }@Override
    public void onClick(View v) {if ("player_btn".equals(v.getTag()))pause();
    }private void init() {mSeekHandler = new SeekHandler();
        mExecutorService = Executors.newSingleThreadExecutor();
        SurfaceView mSurfaceView = new SurfaceView(mContext);
        this.addView(mSurfaceView);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setKeepScreenOn(true);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        addView();

        mMediaPlayer = new MediaPlayer();
        mMediaPlayer.setOnPreparedListener(mPreparedListener);
        mMediaPlayer.setOnCompletionListener(mCompletionListener);
        mMediaPlayer.setOnErrorListener(mErrorListener);
        mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);

    }private void addView() {RelativeLayout mPlayerRl = new RelativeLayout(mContext);
        this.addView(mPlayerRl);
        mPlayerBtn = new Button(mContext);
        mPlayerBtn.setTag("player_btn");
        mPlayerRl.addView(mPlayerBtn);
        mPlayerBtn.setOnClickListener(this);
        mSeekBar = new SeekBar(mContext);
        mPlayerRl.addView(mSeekBar);
        mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
    }private void initPlayer() {try {mMediaPlayer.reset();
            // 指定装载uri
            mMediaPlayer.setDataSource(mContext, Uri.parse(mPath));
            mMediaPlayer.prepareAsync();
            //将所播放的视频图像输出到指定的SurfaceView组件
            mMediaPlayer.setDisplay(mSurfaceHolder);
        } catch (IOException e) {e.printStackTrace();
        }}//MediaPlayer调用prepare()方法时触发该监听器
    private MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {@Override
        public void onPrepared(MediaPlayer mp) {mMediaPlayer.start();
            isPause = false;
            mTotalTime = mMediaPlayer.getDuration();
            mPlayerBtn.setBackgroundResource(R.drawable.v_play_bg);
            mSeekHandler.sendEmptyMessage(PROGRESS_MAX);
            mExecutorService.execute(mRunnable);
        }};

    //MediaPlayer的播放完成事件绑定事件监听器
    private MediaPlayer.OnCompletionListener mCompletionListener = new MediaPlayer.OnCompletionListener() {@Override
        public void onCompletion(MediaPlayer mp) {isPause = true;
            mPlayerBtn.setBackgroundResource(R.drawable.v_stop_bg);
        }};

    //播放错误事件绑定事件监听器
    private MediaPlayer.OnErrorListener mErrorListener = new MediaPlayer.OnErrorListener() {@Override
        public boolean onError(MediaPlayer mp, int what, int extra) {isPause = true;
            mPlayerBtn.setBackgroundResource(R.drawable.v_stop_bg);
            return false;
        }};

    //当MediaPlayer调用seek()方法时触发该监听器
    private MediaPlayer.OnSeekCompleteListener mSeekCompleteListener = new MediaPlayer.OnSeekCompleteListener() {@Override
        public void onSeekComplete(MediaPlayer mp) {// mMediaPlayer.seekTo(curSeekPosition);
        }};

    //通知SeekBar进度被修改SeekBar监听
    private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {@Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}@Override
        public void onStartTrackingTouch(SeekBar seekBar) {}@Override
        public void onStopTrackingTouch(SeekBar seekBar) {}};

    private Runnable mRunnable = new Runnable() {@Override
        public void run() {if (mMediaPlayer != null) {while (mMediaPlayer.getCurrentPosition()<mTotalTime) {if (mMediaPlayer.isPlaying()){Message msg = mSeekHandler.obtainMessage();
                        msg.what = PROGRESS_UPDATE;
                        mSeekHandler.sendMessageDelayed(msg, 500);
                    }}}}};

    private static class SeekHandler extends Handler {@Override
        public void handleMessage(Message msg) {super.handleMessage(msg);
                if (msg.what == PROGRESS_MAX) {mSeekBar.setMax((int) (mTotalTime/100.0));
                } else if (msg.what == PROGRESS_UPDATE) {curSeekPosition= (int) (mMediaPlayer.getCurrentPosition()/100.0);
                    mSeekBar.setProgress(curSeekPosition);
                }}}private void pause() {if (mMediaPlayer != null) {if (!isPause) {isPause = true;
                curSeekPosition = (int) (mMediaPlayer.getCurrentPosition()/100.0);
                mPlayerBtn.setBackgroundResource(R.drawable.v_stop_bg);
                mMediaPlayer.pause();
            } else {isPause = false;
                mPlayerBtn.setBackgroundResource(R.drawable.v_play_bg);
                mMediaPlayer.start();
            }}}//停止播放, 释放资源
    private void stop() {if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {isPause = true;
            mMediaPlayer.stop();
            mMediaPlayer.release();
        }}public static int dp2px(Context context, float dpVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal,
                context.getResources().getDisplayMetrics());
    }}
 

使用方法:

public class VideoActivity extends AppCompatActivity {private LinearLayout mParent;
    private CustomVideoPlayer customVideoPlayer;
    private String path="http://jzvd.nathen.cn/c6e3dc12a1154626b3476d9bf3bd7266/6b56c5f0dc31428083757a45764763b0-5287d2089db37e62345123a1be272f8b.mp4";
    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video);
        mParent = findViewById(R.id.parent_ll);
        customVideoPlayer=new CustomVideoPlayer(this,path);
        LinearLayout.LayoutParams  lp1= new LinearLayout.LayoutParams(CustomCamera.getScreenWidth(this),
                CustomCamera.getScreenHeight(this)/2);
        customVideoPlayer.setLayoutParams(lp1);
        mParent.addView(customVideoPlayer);
    }
}



http://chatgpt.dhexx.cn/article/cF5avbVK.shtml

相关文章

Android相机开发那些坑

版权声明&#xff1a;本文由王梓原创文章&#xff0c;转载请注明出处: 文章原文链接&#xff1a;https://www.qcloud.com/community/article/168 来源&#xff1a;腾云阁 https://www.qcloud.com/community 最近我负责开发了一个跟Android相机有关的需求&#xff0c;新功能允…

Android SurfaceView使用详解(很好的实战例子)

一、surfaceview 在显示时才会调用callback中的surfaceCreated。注意&#xff0c;是在显示时&#xff0c;在初始化时不会调用 在隐藏时会调用callback中的surfaceDestroyed 二、清屏操作 public void clearDraw(SurfaceHolder holder,int color) {Log.w("tan",&qu…

android绘制黑色画面,Android音视频技术入门之绘制一张图片

Android 的音视频入门学习&#xff0c;首先了解一下绘制图片。在 Android 平台绘制一张图片&#xff0c;使用至少 3 种不同的 API&#xff0c;ImageView&#xff0c;SurfaceView&#xff0c;自定义 View作绘制图片。下面我以SurfaceView作重点来讲&#xff0c;为什么不用其他的…

Android图形系统之Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的联系

SurfaceHolder 版本&#xff1a;Android 2.2 r1 结构 继承关系 public interface SurfaceHolder android.view.SurfaceHolder 概述 抽象接口持有人显示表面。允许您控制面的大小和格式&#xff0c;编辑在suface的橡树&#xff0c;并监测到变化。此接口通常可通过SurfaceView类…

Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系

一、Surface Surface就是“表面”的意思。在SDK的文档中&#xff0c;对Surface的描述是这样 的&#xff1a;“Handle onto a raw buffer that is being managed by the screen compositor”&#xff0c;翻译成中文就是“由屏幕显示内容合成器(screen compositor)所管理的原生缓…

android surfaceview学习(一)

推荐阅读&#xff1a; https://blog.csdn.net/luoshengyang/article/details/8661317 https://www.cnblogs.com/xuling/archive/2011/06/06/android.html https://blog.csdn.net/zcmain/article/details/14454953 1、surfaceview默认是黑色的背景&#xff0c;并且给sfv设置背景…

Activity、Fragment和Surface的生命周期

这里首先推荐大家一本Android的学习书籍&#xff1a;《第一行代码》&#xff0c;这本书还是写得非常好的。好了&#xff0c;进入正题&#xff0c;我们先来了解一下Activity、Fragment的的生命周期中经历的几个阶段。 ActivityFragment 下面我们假设有如下的场景&#xff0c;Act…

Java进度条

转自&#xff1a;点击打开链接 进度条用在很多的地方&#xff0c;比如登录验证等待过程、程序加载、数据下载、数据更新与删除等&#xff0c;它可以一目了然的让用户知道程序执行的进度。下面给大家看一个登录验证的进度条&#xff0c;首先上效果图&#xff1a; 在代码中未连接…

java窗口进度条_java进度条窗口具体如何制作?

可视化窗口是大部分游戏的常态&#xff0c;今天我们就来了解下如何编写进度条窗口&#xff0c;快跟小编一起看看吧。 一、在包右击new->other->WindowBuilder->JFrame->name叫Guithreadpackage guithread; import java.awt.BorderLayout; import java.awt.EventQue…

java进度条_「java进度条」Java JProgressBar类(进度条) - seo实验室

java进度条 简介 利用JprogressBar类可以实现一个进度条。它一般是一种颜色部分或完全填充的矩形。缺省情况下,进度条配备了一个凹陷的边框,并水平放置。 进度条还可以选择显示一个字符串,这个字符串在进度条矩形的中央位置上显示。这个字符串缺省时为耗时任务已完成的百分比…

Java 进度条

效果 代码 思路&#xff1a;使用NumberFormat类来格式化数字&#xff0c;显示完后&#xff0c;退格覆盖重新显示新的数字 import java.text.NumberFormat;public class Test {public static void main(String[] args) throws Exception {System.out.print("Progress:"…

Java图形化界面---进度条

目录 一、进度条的介绍 二、创建进度条 三、简单案例 四、子线程创建进度条 &#xff08;1&#xff09;为什么要使用子线程创建进度条 &#xff08;2&#xff09;子线程创建进度条的代码 五、创建进度对话框 &#xff08;1&#xff09;如何创建进度对话框 &#xff08;2&…

Java图形化界面编程超详细知识点(7)——进度条

目录 4 JProgressBar、ProgressMonitor、BoundedRangeModel实现进度条 4.1 创建进度条 4 JProgressBar、ProgressMonitor、BoundedRangeModel实现进度条 进度条是图形界面中广泛使用的GUI组件&#xff0c;当复制一个较大的文件时&#xff0c;操作系统会显示一个进度条&#…

Java制作进度条

【效果图】 【描述】 进度条宽度保持和窗口宽度一致&#xff0c;进度最小值为0&#xff0c;最大值为100&#xff0c;每100毫秒进度值1&#xff0c;进度条中间显示进度百分比 【代码】 package test;import java.awt.*; import java.awt.event.*;import javax.swing.*;public…

QT:QGraphicsScene与QGraphicsView使用render()函数渲染图片

render()函数看官方文档&#xff0c;会有很多不懂得地方&#xff0c;以下为实践出来的效果。 当我们想要用QPaint() 绘制我们在QGraphicsView看到的部分时&#xff0c;不仅要绘制正确的图片大小&#xff0c;还需要使用 render() 渲染正确的大小。当我们只想要绘制坐标系中矩形…

Qt图形特效:QGraphicsOpacityEffect

一、描述 不透明度效果使源具有不透明度。该效果对于使源半透明非常有用&#xff0c;类似于淡入/淡出序列。可以使用setOpacity()函数修改不透明度。 二、属性成员 1、opacity : qreal。此属性保存不透明度。该值应在0.0到1.0的范围内&#xff0c;其中0.0是完全透明的&#…

图形视图(02):【类】QGraphicsScene [官翻]

文章目录 详述事件处理和传播 公共类型enum ItemIndexMethodenum SceneLayer 属性backgroundBrush: QBrushbspTreeDepth: intfocusOnTouch: boolfont: QFontforegroundBrush: QBrushitemIndexMethod: ItemIndexMethodminimumRenderSize: qrealpalette: QPalette**sceneRect**: …

Qt中使用QGraphicsScene重写drawBackGround绘制背景

Qt中使用QGraphicsScene重写drawBackGround绘制背景 需求解释 我是想这学习Qt的界面设计&#xff0c;希望能够对界面背景进行优化然后使用Qt的界面与图形画背景网格。首先查资料 通过查找博客发现很简单&#xff0c;就是新建一个类继承于QGraphicsScene&#xff0c;于是我就…

图形视图框架QGraphicsScene(场景,概念)

QGraphicsScene 该类充当 QGraphicsItems 的容器。它与 QGraphicsView 一起使用&#xff0c;用于在 2D 表面上可视化图形项目&#xff0c;例如线条、矩形、文本甚至自定义项目。 QGraphicsScene具有的功能&#xff1a; 提供用管理大量数据项的高速接口传播事件到每一个图形项…

qt学习笔记(五) QGraphicsPixmapItem与QGraphicsScene的编程实例 图标拖动渐变效果

应大家的要求&#xff0c;还是把完整的工程文件贴出来&#xff0c;大家省点事&#xff1a;http://www.kuaipan.cn/file/id_48923272389086450.htm 先看看运行效果&#xff0c;我用的群创7寸屏&#xff0c;主机是mini2440&#xff0c;分辨率是800*480&#xff0c;程序写比较粗糙…