feat: 增加视频拍摄功能

This commit is contained in:
肖岩(移动开发组) 2021-07-12 09:46:24 +08:00
parent 8fc6ed7d2a
commit e5a65e87c9
9 changed files with 2190 additions and 29 deletions

View File

@ -84,7 +84,7 @@ dependencies {
//
implementation 'com.airsaid.library:pickerview:1.0.3'
// https://github.com/Bigkoo/Android-PickerView
compile 'com.contrarywind:Android-PickerView:4.1.6'
implementation 'com.contrarywind:Android-PickerView:4.1.6'
implementation 'com.contrarywind:Android-PickerView:4.1.8'
/* //html
implementation 'com.github.sendtion:XRichText:1.9.4'*/
@ -94,7 +94,7 @@ dependencies {
implementation 'com.kongzue.dialog_v3x:dialog:3.2.4'
//xRecyclerview下拉刷新控件
implementation project(':xrecyclerview')
//
// https://github.com/natario1/CameraView
implementation 'com.otaliastudios:cameraview:2.7.0'
// https://github.com/umano/AndroidSlidingUpPanel
@ -120,4 +120,7 @@ dependencies {
implementation "androidx.room:room-rxjava2:$room_version"
implementation "androidx.room:room-guava:$room_version"
testImplementation "androidx.room:room-testing:$room_version"
// https://blog.csdn.net/u011520181/article/details/89324292
implementation 'com.googlecode.mp4parser:isoparser:1.1.21'
}

View File

@ -38,6 +38,7 @@
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:largeHeap="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WhiteScreen">

View File

@ -10,6 +10,7 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.location.Location;
import android.media.MediaMetadataRetriever;
import android.os.Looper;
import android.os.Message;
import android.util.DisplayMetrics;
@ -18,21 +19,19 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.Toast;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.navinfo.outdoor.R;
import com.navinfo.outdoor.api.Constant;
import com.navinfo.outdoor.base.BaseActivity;
import com.navinfo.outdoor.util.AWMp4ParserHelper;
import com.navinfo.outdoor.util.FileUtils;
import com.navinfo.outdoor.util.MyTecentLocationSource;
import com.navinfo.outdoor.util.NetWorkUtils;
import com.navinfo.outdoor.util.SdkFolderCreate;
import com.navinfo.outdoor.util.ToastUtil;
import com.otaliastudios.cameraview.CameraException;
import com.otaliastudios.cameraview.CameraListener;
import com.otaliastudios.cameraview.CameraLogger;
@ -44,10 +43,14 @@ import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Mode;
import com.tencent.map.geolocation.TencentLocation;
import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor;
import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.cameraview.size.SizeSelector;
import com.otaliastudios.cameraview.size.SizeSelectors;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdate;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory;
import com.tencent.tencentmap.mapsdk.maps.MapView;
import com.tencent.tencentmap.mapsdk.maps.TencentMap;
import com.tencent.tencentmap.mapsdk.maps.TextureMapView;
import com.tencent.tencentmap.mapsdk.maps.UiSettings;
@ -65,35 +68,60 @@ import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import static com.tencent.tencentmap.mapsdk.maps.model.MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE;
public class PictureActivity extends BaseActivity implements View.OnClickListener {
private static final CameraLogger LOG = CameraLogger.create("Picture");
private CameraView camera;
// private ImageButton capturePicture;
private long captureTime = 0;
private android.widget.Button btnSwitch;
private TencentMap tencentMap;
private MyLocationStyle locationStyle;
private TextureMapView ivMap;
private Button captureVideo, stopVideo;
private Button btnVideo;
private CheckBox captureVideo; // 拍摄视频
private Button btnVideo, stopVideo;
private ImageView ivPic;
private String finalVideoPath; // 摄像后最终保存的文件名
private File tmpFile; // 合并文件的临时文件路径
private Timer timer;
private CsvTimerTask timerTask; // 执行定时写入csv文件的task
private DateFormat formatter;
private final long period = 2; // 记录csv文件的间隔时间单位为秒
@Override
protected int getLayout() {
EventBus.getDefault().register(this);
tmpFile = new File(Constant.PICTURE_FOLDER, "temp.mp4");
formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return R.layout.activity_picture;
}
@Override
protected void initView() {
super.initView();
if (getIntent()!=null) {
finalVideoPath = getIntent().getStringExtra(Constant.INTENT_VIDEO_PATH);
}
if (finalVideoPath == null) {
finalVideoPath = Constant.PICTURE_FOLDER+"/final.mp4";
}
timer = new Timer();
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
ivMap = (TextureMapView) findViewById(R.id.iv_map);
ivPic = findViewById(R.id.iv_pic);
@ -108,13 +136,53 @@ public class PictureActivity extends BaseActivity implements View.OnClickListene
camera = findViewById(R.id.camera);
camera.setOnClickListener(this::onClick);
captureVideo = findViewById(R.id.capuretVideo);
captureVideo.setOnClickListener(this::onClick);
captureVideo.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) { // 开始采集设置按钮文字内容为结束采集
captureVideo.setText("暂停采集");
startTakenVideo(); // 开始采集视频
/**
* 获取当前视频的时间记录csv文件时使用
* */
long currentTime = AWMp4ParserHelper.getInstance().getVedioTotalTime(new File(finalVideoPath));
timerTask = new CsvTimerTask(new File(finalVideoPath+".txt"));
timerTask.setCurrentVideoTime(currentTime/1000);
// 开始采集每隔2秒实时记录位置信息视频时间以及设备时间
timer.schedule(timerTask, 0, period*1000);
} else {
captureVideo.setText("开始采集");
timerTask.cancel();
stopTakenVideo();
}
}
});
stopVideo = findViewById(R.id.btn_stop_video);
stopVideo.setOnClickListener(this::onClick);
//拍照权限
camera.setLifecycleOwner(this);
camera.setEngine(Engine.CAMERA1);
// camera.setVideoBitRate(5);
camera.setPreviewFrameRate(5); // 设置预览视频的帧率
// 设置视频可用的宽高size
SizeSelector width = SizeSelectors.maxWidth(1920);
SizeSelector height = SizeSelectors.maxHeight(1024);
SizeSelector dimensions = SizeSelectors.and(width, height); // Matches sizes bigger than 1000x2000.
SizeSelector ratio = SizeSelectors.aspectRatio(AspectRatio.of(1, 1), 0); // Matches 1:1 sizes.
SizeSelector result = SizeSelectors.or(
SizeSelectors.and(ratio, dimensions), // Try to match both constraints
ratio, // If none is found, at least try to match the aspect ratio
SizeSelectors.biggest() // If none is found, take the biggest
);
camera.setVideoSize(result);
camera.setVideoBitRate(3000*1024);
// camera.setAudioBitRate();
//设置拍照的宽高
SdkFolderCreate.mkdirs(Constant.PICTURE_FOLDER);
@ -127,12 +195,18 @@ public class PictureActivity extends BaseActivity implements View.OnClickListene
// uiSettings.setAllGesturesEnabled(false);
setLocMarkerStyle(); // 设置当前位置显示样式
// camera.addFrameProcessor(new FrameProcessor() {
// @Override
// public void process(@NonNull Frame frame) {
// Log.e("PictureActivity", frame.getTime()+"");
// }
// });
//相机预览监听
camera.addCameraListener(new CameraListener() {
@Override
public void onPictureTaken(@NonNull @NotNull PictureResult result) {
super.onPictureTaken(result);
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
String format = formatter.format(calendar.getTime());
@ -150,15 +224,38 @@ public class PictureActivity extends BaseActivity implements View.OnClickListene
long callbackTime = System.currentTimeMillis();
captureTime = callbackTime - 300;
Log.d("captureTime", captureTime + "");
}
//录像监听
@Override
public void onVideoTaken(@NonNull @NotNull VideoResult result) {
super.onVideoTaken(result);
Toast.makeText(PictureActivity.this, "停止摄像", Toast.LENGTH_SHORT).show();
finish();
Toast.makeText(PictureActivity.this, "暂停摄像", Toast.LENGTH_SHORT).show();
if (result!=null) {
File currentFile = result.getFile();
if (finalVideoPath!=null) { // 有指定的视频文件名称合并文件
if (new File(finalVideoPath).exists()) {
List<String> spliteFileList = new ArrayList<>();
spliteFileList.add(finalVideoPath);
spliteFileList.add(currentFile.getAbsolutePath());
try {
AWMp4ParserHelper.getInstance().mergeVideos(spliteFileList, tmpFile.getAbsolutePath());
if (tmpFile.exists()) {
File finalVideoFile = new File(finalVideoPath);
finalVideoFile.delete();
currentFile.delete();
tmpFile.renameTo(finalVideoFile);
} else {
Toast.makeText(PictureActivity.this, "视频合并失败!", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Log.e("PictureActivity", e.getMessage());
}
} else {
currentFile.renameTo(new File(finalVideoPath));
}
}
}
}
@Override
@ -203,18 +300,47 @@ public class PictureActivity extends BaseActivity implements View.OnClickListene
case R.id.btn_switch:
btnSwich();
break;
case R.id.capuretVideo:
// case R.id.capuretVideo:
// startTakenVideo(); // 开始拍摄视频
// break;
case R.id.btn_stop_video:
this.finish();
break;
}
}
private void startTakenVideo() {
if (camera.isTakingVideo()) {
Toast.makeText(this, "已经在拍摄中...", Toast.LENGTH_SHORT).show();
return;
}
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
String format = formatter.format(calendar.getTime());
String formatVideoName = formatter.format(calendar.getTime());
//文件
File file = new File(Constant.PICTURE_FOLDER, format + ".mp4");
File file = new File(Constant.PICTURE_FOLDER, formatVideoName + ".mp4");
if (file.exists()) {
try {
FileOutputStream fo = new FileOutputStream(file);
FileDescriptor fileDescriptor = fo.getFD();
camera.takeVideo(fileDescriptor);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else {
camera.takeVideo(file);
break;
case R.id.btn_stop_video:
}
}
private void stopTakenVideo() {
if (camera.isTakingVideo()) {
camera.stopVideo();
}
}
private Bitmap getBitMap(int resourceId) {
@ -292,6 +418,7 @@ public class PictureActivity extends BaseActivity implements View.OnClickListene
@Override
protected void onDestroy() {
super.onDestroy();
stopTakenVideo();
camera.destroy();
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
@ -315,4 +442,40 @@ public class PictureActivity extends BaseActivity implements View.OnClickListene
}
}
/**
* 记录视频拍摄的时间及经纬度
*/
private class CsvTimerTask extends TimerTask {
private File csvFile;
private long currentVideoTime; // 记录当前的视频时间
public CsvTimerTask(File csvFile) {
this.csvFile = csvFile;
if (!csvFile.exists()) {
csvFile.getParentFile().mkdirs();
}
}
public void setCurrentVideoTime(long currentVideoTime) {
this.currentVideoTime = currentVideoTime;
}
@Override
public void run() {
// 记录当前时间视频时间以及当前经纬度信息
StringBuffer sb = new StringBuffer();
sb.append(formatter.format(new Date())); // 记录当前时间
sb.append(",");
sb.append(currentVideoTime) ;// 记录视频时间
sb.append(",");
sb.append(Constant.currentLocation.getLatitude());
sb.append(",");
sb.append(Constant.currentLocation.getLongitude());
sb.append("\r\n");
FileUtils.writeFile(csvFile.getAbsolutePath(), sb.toString(), true);
currentVideoTime = currentVideoTime+period; //
}
}
}

View File

@ -94,5 +94,5 @@ public class Constant {
public static JobSearchBean jobSearchBean;//数据
public static MapView treasureMap;
public static final String INTENT_VIDEO_PATH = "INTENT_VIDEO_PATH"; // 拍照界面指定的视频文件保存位置
}

View File

@ -0,0 +1,185 @@
package com.navinfo.outdoor.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.util.Log;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.bitmap.VideoDecoder;
import com.bumptech.glide.request.RequestOptions;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.AppendTrack;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Author: AlanWang4523.
* Date: 19/4/15 01:46.
* Mail: alanwang4523@gmail.com
*/
public class AWMp4ParserHelper {
private static AWMp4ParserHelper instance;
public static AWMp4ParserHelper getInstance() {
if (instance == null) {
instance = new AWMp4ParserHelper();
}
return instance;
}
private final static String PREFIX_VIDEO_HANDLER = "vide";
private final static String PREFIX_AUDIO_HANDLER = "soun";
/**
* 合并视频
* @param inputVideos
* @param outputPath
* @throws IOException
*/
public static void mergeVideos(List<String> inputVideos, String outputPath) throws IOException {
List<Movie> inputMovies = new ArrayList<>();
for (String input : inputVideos) {
inputMovies.add(MovieCreator.build(input));
}
List<Track> videoTracks = new LinkedList<>();
List<Track> audioTracks = new LinkedList<>();
for (Movie m : inputMovies) {
for (Track t : m.getTracks()) {
if (PREFIX_AUDIO_HANDLER.equals(t.getHandler())) {
audioTracks.add(t);
}
if (PREFIX_VIDEO_HANDLER.equals(t.getHandler())) {
videoTracks.add(t);
}
}
}
Movie outputMovie = new Movie();
if (audioTracks.size() > 0) {
outputMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
outputMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
Container out = new DefaultMp4Builder().build(outputMovie);
FileChannel fc = new RandomAccessFile(outputPath, "rw").getChannel();
out.writeContainer(fc);
fc.close();
}
/**
* 获取mp4视频播放时长
* */
/**
* 返回视频播放总时长
* @param vedioFile
* @return
*/
public long getVedioTotalTime(File vedioFile){
if (!vedioFile.exists()){
Log.e("getVedioTotalTime","视频文件不存在");
return 0;
}
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(vedioFile.getAbsolutePath());
String timeString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
long time = Long.valueOf(timeString);
return time;
}
/**
* 获取网络视频第一帧
*
* @param videoUrl
* @return
*/
public void loadFirst(String videoUrl, @NonNull ImageView cover) {
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
//根据url获取缩略图
retriever.setDataSource(videoUrl, new HashMap());
//获得第一帧图片
bitmap = retriever.getFrameAtTime();
} catch (IllegalArgumentException e) {
//e.printStackTrace();
Log.e("zhu", e.toString());
} finally {
retriever.release();
}
if (bitmap != null) {
cover.setImageBitmap(bitmap);
}
}
/**
*   context 上下文
*   uri 视频地址
*   imageView 设置image
*   frameTimeMicros 获取某一时间帧
*/
public void loadFirstWithGlide(final Context context, String uri, ImageView imageView, long frameTimeMicros) {
RequestOptions requestOptions = RequestOptions.frameOf(frameTimeMicros);
requestOptions.set(VideoDecoder.FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST);
requestOptions.transform(new BitmapTransformation() {
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
return toTransform;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
try {
messageDigest.update((context.getPackageName() + "RotateTransform").getBytes("utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
});
Glide.with(context).load(uri).apply(requestOptions).into(imageView);
}
/**
* 获取本地视频的第一帧
*
* @param localPath
* @return
*/
public Bitmap getLocalVideoBitmap(String localPath) {
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
//根据文件路径获取缩略图
retriever.setDataSource(localPath);
//获得第一帧图片
bitmap = retriever.getFrameAtTime();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} finally {
retriever.release();
}
return bitmap;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
package com.navinfo.outdoor.util;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* IO utils
*
* @author Vladislav Bauer
*/
public class IOUtils {
private IOUtils() {
throw new AssertionError();
}
/**
* Close closable object and wrap {@link IOException} with {@link
* RuntimeException}
*
* @param closeable closeable object
*/
public static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
}
}
}
/**
* Close closable and hide possible {@link IOException}
*
* @param closeable closeable object
*/
public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
// Ignored
}
}
}
/**
*保存文本
* @param fileName 文件名字
* @param content 内容
* @param append 是否累加
* @return 是否成功
*/
public static boolean saveTextValue(String fileName, String content, boolean append) {
try {
File textFile = new File(fileName);
if (!append && textFile.exists()) textFile.delete();
FileOutputStream os = new FileOutputStream(textFile);
os.write(content.getBytes("UTF-8"));
os.close();
} catch (Exception ee) {
return false;
}
return true;
}
/**
* 删除目录下所有文件
* @param Path 路径
*/
public static void deleteAllFile(String Path) {
// 删除目录下所有文件
File path = new File(Path);
File files[] = path.listFiles();
if (files != null) {
for (File tfi : files) {
if (tfi.isDirectory()) {
System.out.println(tfi.getName());
}
else {
tfi.delete();
}
}
}
}
}

View File

@ -0,0 +1,703 @@
package com.navinfo.outdoor.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <h2>字符串工具类提供一些字符串相关的便捷方法</h2>
*/
public class StringUtils {
private StringUtils() {
throw new AssertionError();
}
/**
* is null or its length is 0 or it is made by space
*
* <pre>
* isBlank(null) = true;
* isBlank(&quot;&quot;) = true;
* isBlank(&quot; &quot;) = true;
* isBlank(&quot;a&quot;) = false;
* isBlank(&quot;a &quot;) = false;
* isBlank(&quot; a&quot;) = false;
* isBlank(&quot;a b&quot;) = false;
* </pre>
*
* @param str str
* @return if string is null or its size is 0 or it is made by space, return
* true, else return false.
*/
public static boolean isBlank(String str) {
return (str == null || str.trim().length() == 0);
}
/**
* is null or its length is 0
*
* <pre>
* isEmpty(null) = true;
* isEmpty(&quot;&quot;) = true;
* isEmpty(&quot; &quot;) = false;
* </pre>
*
* @param str str
* @return if string is null or its size is 0, return true, else return
* false.
*/
public static boolean isEmpty(CharSequence str) {
return (str == null || str.length() == 0);
}
/**
* get length of CharSequence
*
* <pre>
* length(null) = 0;
* length(\"\") = 0;
* length(\"abc\") = 3;
* </pre>
*
* @param str str
* @return if str is null or empty, return 0, else return {@link
* CharSequence#length()}.
*/
public static int length(CharSequence str) {
return str == null ? 0 : str.length();
}
/**
* null Object to empty string
*
* <pre>
* nullStrToEmpty(null) = &quot;&quot;;
* nullStrToEmpty(&quot;&quot;) = &quot;&quot;;
* nullStrToEmpty(&quot;aa&quot;) = &quot;aa&quot;;
* </pre>
*
* @param str str
* @return String
*/
public static String nullStrToEmpty(Object str) {
return (str == null
? ""
: (str instanceof String ? (String) str : str.toString()));
}
/**
* @param str str
* @return String
*/
public static String capitalizeFirstLetter(String str) {
if (isEmpty(str)) {
return str;
}
char c = str.charAt(0);
return (!Character.isLetter(c) || Character.isUpperCase(c))
? str
: new StringBuilder(str.length()).append(
Character.toUpperCase(c))
.append(str.substring(1))
.toString();
}
/**
* encoded in utf-8
*
* @param str 字符串
* @return 返回一个utf8的字符串
*/
public static String utf8Encode(String str) {
if (!isEmpty(str) && str.getBytes().length != str.length()) {
try {
return URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(
"UnsupportedEncodingException occurred. ", e);
}
}
return str;
}
/**
* @param href 字符串
* @return 返回一个html
*/
public static String getHrefInnerHtml(String href) {
if (isEmpty(href)) {
return "";
}
String hrefReg = ".*<[\\s]*a[\\s]*.*>(.+?)<[\\s]*/a[\\s]*>.*";
Pattern hrefPattern = Pattern.compile(hrefReg,
Pattern.CASE_INSENSITIVE);
Matcher hrefMatcher = hrefPattern.matcher(href);
if (hrefMatcher.matches()) {
return hrefMatcher.group(1);
}
return href;
}
/**
* @param source 字符串
* @return 返回htmL到字符串
*/
public static String htmlEscapeCharsToString(String source) {
return StringUtils.isEmpty(source)
? source
: source.replaceAll("&lt;", "<")
.replaceAll("&gt;", ">")
.replaceAll("&amp;", "&")
.replaceAll("&quot;", "\"");
}
/**
* @param s str
* @return String
*/
public static String fullWidthToHalfWidth(String s) {
if (isEmpty(s)) {
return s;
}
char[] source = s.toCharArray();
for (int i = 0; i < source.length; i++) {
if (source[i] == 12288) {
source[i] = ' ';
// } else if (source[i] == 12290) {
// source[i] = '.';
}
else if (source[i] >= 65281 && source[i] <= 65374) {
source[i] = (char) (source[i] - 65248);
}
else {
source[i] = source[i];
}
}
return new String(source);
}
/**
* @param s 字符串
* @return 返回的数值
*/
public static String halfWidthToFullWidth(String s) {
if (isEmpty(s)) {
return s;
}
char[] source = s.toCharArray();
for (int i = 0; i < source.length; i++) {
if (source[i] == ' ') {
source[i] = (char) 12288;
// } else if (source[i] == '.') {
// source[i] = (char)12290;
}
else if (source[i] >= 33 && source[i] <= 126) {
source[i] = (char) (source[i] + 65248);
}
else {
source[i] = source[i];
}
}
return new String(source);
}
/**
* @param str 资源
* @return 特殊字符串切换
*/
public static String replaceBlanktihuan(String str) {
String dest = "";
if (str != null) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(str);
dest = m.replaceAll("");
}
return dest;
}
/**
* 判断给定的字符串是否为null或者是空的
*
* @param string 给定的字符串
*/
public static boolean isEmpty(String string) {
return string == null || "".equals(string.trim());
}
/**
* 判断给定的字符串是否不为null且不为空
*
* @param string 给定的字符串
*/
public static boolean isNotEmpty(String string) {
return !isEmpty(string);
}
/**
* 判断给定的字符串数组中的所有字符串是否都为null或者是空的
*
* @param strings 给定的字符串
*/
public static boolean isEmpty(String... strings) {
boolean result = true;
for (String string : strings) {
if (isNotEmpty(string)) {
result = false;
break;
}
}
return result;
}
/**
* 判断给定的字符串数组中是否全部都不为null且不为空
*
* @param strings 给定的字符串数组
* @return 是否全部都不为null且不为空
*/
public static boolean isNotEmpty(String... strings) {
boolean result = true;
for (String string : strings) {
if (isEmpty(string)) {
result = false;
break;
}
}
return result;
}
/**
* 如果字符串是null或者空就返回""
*/
public static String filterEmpty(String string) {
return StringUtils.isNotEmpty(string) ? string : "";
}
/**
* 在给定的字符串中用新的字符替换所有旧的字符
*
* @param string 给定的字符串
* @param oldchar 旧的字符
* @param newchar 新的字符
* @return 替换后的字符串
*/
public static String replace(String string, char oldchar, char newchar) {
char chars[] = string.toCharArray();
for (int w = 0; w < chars.length; w++) {
if (chars[w] == oldchar) {
chars[w] = newchar;
break;
}
}
return new String(chars);
}
/**
* 把给定的字符串用给定的字符分割
*
* @param string 给定的字符串
* @param ch 给定的字符
* @return 分割后的字符串数组
*/
public static String[] split(String string, char ch) {
ArrayList<String> stringList = new ArrayList<String>();
char chars[] = string.toCharArray();
int nextStart = 0;
for (int w = 0; w < chars.length; w++) {
if (ch == chars[w]) {
stringList.add(new String(chars, nextStart, w - nextStart));
nextStart = w + 1;
if (nextStart ==
chars.length) { //当最后一位是分割符的话就再添加一个空的字符串到分割数组中去
stringList.add("");
}
}
}
if (nextStart <
chars.length) { //如果最后一位不是分隔符的话就将最后一个分割符到最后一个字符中间的左右字符串作为一个字符串添加到分割数组中去
stringList.add(new String(chars, nextStart,
chars.length - 1 - nextStart + 1));
}
return stringList.toArray(new String[stringList.size()]);
}
/**
* 计算给定的字符串的长度计算规则是一个汉字的长度为2一个字符的长度为1
*
* @param string 给定的字符串
* @return 长度
*/
public static int countLength(String string) {
int length = 0;
char[] chars = string.toCharArray();
for (int w = 0; w < string.length(); w++) {
char ch = chars[w];
if (ch >= '\u0391' && ch <= '\uFFE5') {
length++;
length++;
}
else {
length++;
}
}
return length;
}
private static char[] getChars(char[] chars, int startIndex) {
int endIndex = startIndex + 1;
//如果第一个是数字
if (Character.isDigit(chars[startIndex])) {
//如果下一个是数字
while (endIndex < chars.length &&
Character.isDigit(chars[endIndex])) {
endIndex++;
}
}
char[] resultChars = new char[endIndex - startIndex];
System.arraycopy(chars, startIndex, resultChars, 0, resultChars.length);
return resultChars;
}
/**
* 是否全是数字
*/
public static boolean isAllDigital(char[] chars) {
boolean result = true;
for (int w = 0; w < chars.length; w++) {
if (!Character.isDigit(chars[w])) {
result = false;
break;
}
}
return result;
}
/**
* 删除给定字符串中所有的旧的字符
*
* @param string 源字符串
* @param ch 要删除的字符
* @return 删除后的字符串
*/
public static String removeChar(String string, char ch) {
StringBuffer sb = new StringBuffer();
for (char cha : string.toCharArray()) {
if (cha != '-') {
sb.append(cha);
}
}
return sb.toString();
}
/**
* 删除给定字符串中给定位置处的字符
*
* @param string 给定字符串
* @param index 给定位置
*/
public static String removeChar(String string, int index) {
String result = null;
char[] chars = string.toCharArray();
if (index == 0) {
result = new String(chars, 1, chars.length - 1);
}
else if (index == chars.length - 1) {
result = new String(chars, 0, chars.length - 1);
}
else {
result = new String(chars, 0, index) +
new String(chars, index + 1, chars.length - index);
;
}
return result;
}
/**
* 删除给定字符串中给定位置处的字符
*
* @param string 给定字符串
* @param index 给定位置
* @param ch 如果同给定位置处的字符相同则将给定位置处的字符删除
*/
public static String removeChar(String string, int index, char ch) {
String result = null;
char[] chars = string.toCharArray();
if (chars.length > 0 && chars[index] == ch) {
if (index == 0) {
result = new String(chars, 1, chars.length - 1);
}
else if (index == chars.length - 1) {
result = new String(chars, 0, chars.length - 1);
}
else {
result = new String(chars, 0, index) +
new String(chars, index + 1, chars.length - index);
;
}
}
else {
result = string;
}
return result;
}
/**
* 对给定的字符串进行空白过滤
*
* @param string 给定的字符串
* @return 如果给定的字符串是一个空白字符串那么返回null否则返回本身
*/
public static String filterBlank(String string) {
if ("".equals(string)) {
return null;
}
else {
return string;
}
}
/**
* 将给定字符串中给定的区域的字符转换成小写
*
* @param str 给定字符串中
* @param beginIndex 开始索引包括
* @param endIndex 结束索引不包括
* @return 新的字符串
*/
public static String toLowerCase(String str, int beginIndex, int endIndex) {
return str.replaceFirst(str.substring(beginIndex, endIndex),
str.substring(beginIndex, endIndex)
.toLowerCase(Locale.getDefault()));
}
/**
* 将给定字符串中给定的区域的字符转换成大写
*
* @param str 给定字符串中
* @param beginIndex 开始索引包括
* @param endIndex 结束索引不包括
* @return 新的字符串
*/
public static String toUpperCase(String str, int beginIndex, int endIndex) {
return str.replaceFirst(str.substring(beginIndex, endIndex),
str.substring(beginIndex, endIndex)
.toUpperCase(Locale.getDefault()));
}
/**
* 将给定字符串的首字母转为小写
*
* @param str 给定字符串
* @return 新的字符串
*/
public static String firstLetterToLowerCase(String str) {
return toLowerCase(str, 0, 1);
}
/**
* 将给定字符串的首字母转为大写
*
* @param str 给定字符串
* @return 新的字符串
*/
public static String firstLetterToUpperCase(String str) {
return toUpperCase(str, 0, 1);
}
/**
* 将给定的字符串MD5加密
*
* @param string 给定的字符串
* @return MD5加密后生成的字符串
*/
public static String MD5(String string) {
String result = null;
try {
char[] charArray = string.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++) {
byteArray[i] = (byte) charArray[i];
}
StringBuffer hexValue = new StringBuffer();
byte[] md5Bytes = MessageDigest.getInstance("MD5")
.digest(byteArray);
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
result = hexValue.toString();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 判断给定的字符串是否以一个特定的字符串开头忽略大小写
*
* @param sourceString 给定的字符串
* @param newString 一个特定的字符串
*/
public static boolean startsWithIgnoreCase(String sourceString, String newString) {
int newLength = newString.length();
int sourceLength = sourceString.length();
if (newLength == sourceLength) {
return newString.equalsIgnoreCase(sourceString);
}
else if (newLength < sourceLength) {
char[] newChars = new char[newLength];
sourceString.getChars(0, newLength, newChars, 0);
return newString.equalsIgnoreCase(String.valueOf(newChars));
}
else {
return false;
}
}
/**
* 判断给定的字符串是否以一个特定的字符串结尾忽略大小写
*
* @param sourceString 给定的字符串
* @param newString 一个特定的字符串
*/
public static boolean endsWithIgnoreCase(String sourceString, String newString) {
int newLength = newString.length();
int sourceLength = sourceString.length();
if (newLength == sourceLength) {
return newString.equalsIgnoreCase(sourceString);
}
else if (newLength < sourceLength) {
char[] newChars = new char[newLength];
sourceString.getChars(sourceLength - newLength, sourceLength,
newChars, 0);
return newString.equalsIgnoreCase(String.valueOf(newChars));
}
else {
return false;
}
}
/**
* 检查字符串长度如果字符串的长度超过maxLength就截取前maxLength个字符串并在末尾拼上appendString
*/
public static String checkLength(String string, int maxLength, String appendString) {
if (string.length() > maxLength) {
string = string.substring(0, maxLength);
if (appendString != null) {
string += appendString;
}
}
return string;
}
/**
* 检查字符串长度如果字符串的长度超过maxLength就截取前maxLength个字符串并在末尾拼上
*/
public static String checkLength(String string, int maxLength) {
return checkLength(string, maxLength, "");
}
/**
* 删除Html标签
*
* @param inputString
* @return
*/
public static String htmlRemoveTag(String inputString) {
if (inputString == null)
return null;
String htmlStr = inputString; // 含html标签的字符串
String textStr = "";
java.util.regex.Pattern p_script;
java.util.regex.Matcher m_script;
java.util.regex.Pattern p_style;
java.util.regex.Matcher m_style;
java.util.regex.Pattern p_html;
java.util.regex.Matcher m_html;
try {
//定义script的正则表达式{<script[^>]*?>[\\s\\S]*?<\\/script>
String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
//定义style的正则表达式{<style[^>]*?>[\\s\\S]*?<\\/style>
String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>";
String regEx_html = "<[^>]+>"; // 定义HTML标签的正则表达式
p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
m_script = p_script.matcher(htmlStr);
htmlStr = m_script.replaceAll(""); // 过滤script标签
p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
m_style = p_style.matcher(htmlStr);
htmlStr = m_style.replaceAll(""); // 过滤style标签
p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
m_html = p_html.matcher(htmlStr);
htmlStr = m_html.replaceAll(""); // 过滤html标签
textStr = htmlStr;
} catch (Exception e) {
e.printStackTrace();
}
return textStr;// 返回文本字符串
}
}

View File

@ -55,7 +55,7 @@
app:layout_constraintTop_toTopOf="parent" />
<Button
<CheckBox
android:id="@+id/capuretVideo"
android:layout_width="100dp"
android:layout_height="wrap_content"
@ -64,6 +64,10 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginBottom="20dp"
android:background="@drawable/user_style"
android:button="@null"
android:gravity="center"
android:padding="@dimen/fab_margin"
style="@style/user_data_style"/>
<Button
android:id="@+id/btn_stop_video"
@ -72,6 +76,7 @@
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="结束采集"
android:visibility="gone"
app:layout_constraintLeft_toRightOf="@id/capuretVideo"
android:background="@drawable/uploding_shape"
android:textColor="@color/colorBlue"/>