V1.0.0(2),优化

This commit is contained in:
lihongwei 2024-09-03 18:07:05 +08:00
parent 76b26d0126
commit c660170265
55 changed files with 2485 additions and 225 deletions

View File

@ -6,6 +6,10 @@ android {
namespace = "com.sound.prankparty"
compileSdk = 34
buildFeatures {
viewBinding = true
}
defaultConfig {
applicationId = "com.sound.prankparty.test"
minSdk = 23

View File

@ -4,14 +4,16 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:name=".Application.MainApplication"
android:name="com.sound.MainApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@ -22,23 +24,26 @@
android:theme="@style/Theme.PrankPartyDemo"
tools:targetApi="31">
<activity
android:name=".SecondFragment.SaveRecordActivity"
android:name=".Activity.PrivacyActivity"
android:exported="false" />
<activity
android:name=".SecondFragment.NewRecordActivity"
android:name=".Activity.SaveRecordActivity"
android:exported="false" />
<activity
android:name=".FirstFragment.PlaySoundActivity"
android:name=".Activity.NewRecordActivity"
android:exported="false" />
<activity
android:name=".FirstFragment.AirHornActivity"
android:name=".Activity.PlaySoundActivity"
android:exported="false" />
<activity
android:name=".StartUp.SplashActivity"
android:name=".Activity.AirHornActivity"
android:exported="false" />
<activity
android:name=".Activity.SplashActivity"
android:exported="false"
android:theme="@style/Theme.StartUp" />
<activity
android:name=".Main.MainActivity"
android:name=".Activity.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Privacy Policy</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
padding: 20px;
}
h1, h2 {
color: #333;
}
h2 {
margin-top: 20px;
}
p {
margin: 10px 0;
}
ul {
margin: 10px 0;
padding-left: 20px;
}
</style>
</head>
<body>
<h1>Privacy Policy</h1>
<p><strong>Date:</strong> August 9, 2024</p>
<p>We provide this Privacy Policy to help you understand how we collect, use, and disclose information, including what you may provide to us or that we obtain from our products and services. We treat your privacy very seriously. Your privacy is important to us.</p>
<h2>Information Collection and Use</h2>
<p>For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information, such as users' name, address, location, pictures, etc. The information that we request will be retained on your device and is not collected or retained by us in any way and used as described in this privacy policy.</p>
<p>The app does use third-party services that may collect information used to identify you.</p>
<p>Please refer to the privacy policy of the third-party service provider used by the application:</p>
<ul>
<li>Google Play Services</li>
<li>Google Analytics for Firebase</li>
<li>Firebase Crashlytics</li>
<li>Unity</li>
<li>AppLovin</li>
<li>Pangle</li>
<li>Mintegral</li>
<li>Bigo</li>
<li>Ironsource</li>
<li>Vungle</li>
</ul>
<h2>Log Data</h2>
<p>We want to inform you that whenever you use our Service, in case of an error in the app, we collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing our Service, the time and date of your use of the Service, and other statistics.</p>
<h2>Cookies</h2>
<p>Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.</p>
<p>This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.</p>
<h2>Service Providers</h2>
<p>We may employ third-party companies and individuals due to the following reasons:</p>
<ul>
<li>To facilitate our Service</li>
<li>To provide the Service on our behalf</li>
<li>To perform Service-related services</li>
<li>To assist us in analyzing how our Service is used</li>
</ul>
<p>We want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.</p>
<h2>Security</h2>
<p>We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.</p>
<h2>Links to Other Sites</h2>
<p>This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.</p>
<h2>Childrens Privacy</h2>
<p>These Services do not address anyone under the age of 13. We do not knowingly collect personally identifiable information from children under 13 years of age. In the case we discover that a child under 13 has provided us with personal information, we will immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact us so that we will be able to take the necessary actions.</p>
<h2>Changes to This Privacy Policy</h2>
<p>We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page.</p>
<h2>Privacy Questions</h2>
<p>We may update the Privacy Policy from time to time. When we change the policy in a material way, a notice will be posted on our website along with the updated Privacy Policy.</p>
<p>If you have any questions or concerns about our Privacy Policy or data processing, please contact us: <a href="elizavetasalutina@gmail.com">elizavetasalutina@gmail.com</a>.</p>
</body>
</html>

View File

@ -0,0 +1,33 @@
package com.sound;
import android.app.Application;
import android.content.Context;
public class MainApplication extends Application {
/**
* 全局的上下文
*/
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
//获取应用的上下文并赋值给 mContext
mContext = getApplicationContext();
}
/**
* 获取context
* @return
*/
public static Context getContext(){
return mContext;
}
//重写 onLowMemory 方法在系统内存不足时调用这里只是调用了父类的 onLowMemory 方法没有做其他处理
@Override
public void onLowMemory() {
super.onLowMemory();
}
}

View File

@ -0,0 +1,51 @@
package com.sound.prankparty.Activity;
import android.graphics.Color;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import com.sound.prankparty.Adapter.AirHornRecyclerViewAdapter;
import com.sound.prankparty.JSON.Category;
import com.sound.prankparty.JSON.SoundItem;
import com.sound.prankparty.Utils.ItemDecoration;
import com.sound.prankparty.databinding.ActivityAirhonrBinding;
import java.util.List;
public class AirHornActivity extends AppCompatActivity {
private ActivityAirhonrBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityAirhonrBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.airHornRecyclerview.setLayoutManager(new GridLayoutManager(this, 3));
// 接收传递的 List<Category> 对象
Category categories = (Category) getIntent().getSerializableExtra("123");
// 获取颜色值
int receivedColor = getIntent().getIntExtra("color", Color.TRANSPARENT); // 默认值为透明色
if (categories == null) {
// 处理 categories null 的情况
return;
}
List<SoundItem> soundItemList = categories.getSoundItemList();
String categoryName = categories.getCategoryName();
binding.airHornRecyclerview.setAdapter(new AirHornRecyclerViewAdapter(this, soundItemList,receivedColor));
binding.airHornTitle.setText(categoryName);
binding.airHornBack.setOnClickListener(v -> finish());
// RecyclerView 添加自定义的间距装饰
ItemDecoration itemDecoration = new ItemDecoration(16, 19, 10);
binding.airHornRecyclerview.addItemDecoration(itemDecoration);
}
}

View File

@ -0,0 +1,87 @@
package com.sound.prankparty.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.sound.prankparty.Adapter.MainViewPager2Adapter;
import com.sound.prankparty.R;
import com.sound.prankparty.databinding.ActivityMainBinding;
import com.sound.prankparty.databinding.MainTabItemCustomBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
MainViewPager2Adapter adapter = new MainViewPager2Adapter(this);
binding.mainViewpager.setAdapter(adapter);
new TabLayoutMediator(binding.mainTablayout, binding.mainViewpager, (tab, position) -> {
MainTabItemCustomBinding tabBinding = MainTabItemCustomBinding.inflate(LayoutInflater.from(this));
tab.setCustomView(tabBinding.getRoot());
if (position == 0) {
tabBinding.iconCustom.setImageResource(R.drawable.main_sound_select);
} else if (position == 1) {
tabBinding.iconCustom.setImageResource(R.drawable.main_radio_unselect);
} else {
tabBinding.iconCustom.setImageResource(R.drawable.main_favorite_unselect);
}
}).attach();
//设置标签选中事件
binding.mainTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if (tab.getCustomView() != null) {
MainTabItemCustomBinding tabBinding = MainTabItemCustomBinding.bind(tab.getCustomView());
if (tab.getPosition() == 0) {
tabBinding.iconCustom.setImageResource(R.drawable.main_sound_select);
} else if (tab.getPosition() == 1) {
tabBinding.iconCustom.setImageResource(R.drawable.main_radio_select);
} else {
tabBinding.iconCustom.setImageResource(R.drawable.main_favorite_select);
}
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
if (tab.getCustomView() != null) {
MainTabItemCustomBinding tabBinding = MainTabItemCustomBinding.bind(tab.getCustomView());
if (tab.getPosition() == 0) {
tabBinding.iconCustom.setImageResource(R.drawable.main_sound_unselect);
} else if (tab.getPosition() == 1) {
tabBinding.iconCustom.setImageResource(R.drawable.main_radio_unselect);
} else {
tabBinding.iconCustom.setImageResource(R.drawable.main_favorite_unselect);
}
}
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// 处理Tab重新选中事件
}
});
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
// 返回到ViewPager2的当前Fragment
binding.mainViewpager.setCurrentItem(binding.mainViewpager.getCurrentItem());
}
}
}

View File

@ -0,0 +1,145 @@
package com.sound.prankparty.Activity;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.sound.prankparty.databinding.ActivityNewRecordBinding;
import java.io.File;
import java.io.IOException;
public class NewRecordActivity extends AppCompatActivity {
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
private ActivityNewRecordBinding binding;
private Boolean isStarted = false;
private Handler handler = new Handler();
private long startTime;
private MediaRecorder mediaRecorder;
private File outputFile;
private Runnable updateTimeTask = new Runnable() {
@Override
public void run() {
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - startTime;
binding.newRecordTime.setText(formatTime(elapsedTime));
handler.postDelayed(this, 1000);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityNewRecordBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.newRecordRecord.setOnClickListener(v -> {
// 检查是否授予了RECORD_AUDIO权限
if (ContextCompat.checkSelfPermission(NewRecordActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// 如果未被授予则请求该权限
ActivityCompat.requestPermissions(NewRecordActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_RECORD_AUDIO_PERMISSION);
} else {
// 如果获得权限就开始录音
toggleRecording();
}
});
binding.newRecordBack.setOnClickListener(v -> finish());
}
private void toggleRecording() {
if (isStarted) {
// 停止录音
stopRecording();
} else {
// 开始录音
startRecording();
}
isStarted = !isStarted; // 切换录音状态
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 获得许可开始录音
toggleRecording();
} else {
// 权限被拒绝通知用户
Toast.makeText(this, "录音权限被拒绝,请在设置中启用权限", Toast.LENGTH_SHORT).show();
}
}
}
private void startRecording() {
try {
// 初始化MediaRecorder
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
// 设置输出文件路径
String fileName = "recording_" + System.currentTimeMillis() + ".m4a";
outputFile = new File(getFilesDir(), fileName);
mediaRecorder.setOutputFile(outputFile.getAbsolutePath());
// 准备并开始录音
mediaRecorder.prepare();
mediaRecorder.start();
// 更新UI
startTime = System.currentTimeMillis();
handler.post(updateTimeTask);
binding.animationView.playAnimation();
} catch (IOException e) {
e.printStackTrace();
// 处理异常
}
}
private void stopRecording() {
// 停止录音并释放MediaRecorder
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
}
// 更新UI
handler.removeCallbacks(updateTimeTask);
binding.animationView.cancelAnimation();
binding.animationView.setFrame(0);
binding.newRecordTime.setText("00:00:00");
Intent intent = new Intent(NewRecordActivity.this, SaveRecordActivity.class);
intent.putExtra("audioFilePath", outputFile.getAbsolutePath());
startActivity(intent);
}
private String formatTime(long elapsedTime) {
int seconds = (int) (elapsedTime / 1000) % 60;
int minutes = (int) ((elapsedTime / (1000 * 60)) % 60);
int hours = (int) ((elapsedTime / (1000 * 60 * 60)) % 24);
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(updateTimeTask);
}
}

View File

@ -0,0 +1,286 @@
package com.sound.prankparty.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.SeekBar;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.sound.MainApplication;
import com.sound.prankparty.JSON.SoundItem;
import com.sound.prankparty.R;
import com.sound.prankparty.Room.AppDatabase;
import com.sound.prankparty.Room.FavoriteSounds;
import com.sound.prankparty.Room.FavoriteSoundsDao;
import com.sound.prankparty.databinding.ActivityPlaySoundBinding;
import java.io.IOException;
public class PlaySoundActivity extends AppCompatActivity {
private MediaPlayer mediaPlayer;
private SoundItem soundItemList;
private AudioManager audioManager;
private boolean isPlaying = false; // 默认未播放
private boolean isLooping = false; // 默认不循环
private AppDatabase appDatabase;
private FavoriteSoundsDao favoriteSoundsDao;
private boolean isFavorite = false; // 默认不收藏
private ActivityPlaySoundBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityPlaySoundBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// 获取音频管理器
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
appDatabase = AppDatabase.getDatabase(this);
favoriteSoundsDao = appDatabase.favoriteSoundsDao();
// 接收传递的 SoundItem 对象
soundItemList = (SoundItem) getIntent().getSerializableExtra("1234");
if (soundItemList == null) {
return;
}
Log.d("qweeq", soundItemList.getMp3Url());
binding.text.setText(soundItemList.getTitle());
binding.back.setOnClickListener(v -> finish());
// 检查数据库中是否存在该音频的收藏记录
checkFavoriteStatus();
// 设置收藏点击事件
binding.favorite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
isFavorite = !isFavorite;
updateFavoriteIcon();
updateDatabase();
}
});
// 设置循环图标的点击事件
binding.loop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
isLooping = !isLooping;
binding.loop.setImageResource(isLooping ? R.drawable.true_loop : R.drawable.loop);
if (mediaPlayer != null) {
mediaPlayer.setLooping(isLooping);
}
}
});
// 设置播放/暂停图标的点击事件
binding.playback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playAudio();
}
});
// 获取设备的最大音量值
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
// 设置SeekBar的最大值
binding.progress.setMax(maxVolume);
// 获取当前音量值
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
// 设置SeekBar的初始进度
binding.progress.setProgress(currentVolume);
// 设置SeekBar监听器
binding.progress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// 调整音量
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 用户开始拖动进度条时执行的操作
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 用户停止拖动进度条时执行的操作
}
});
// 注册广播接收器
IntentFilter filter = new IntentFilter("android.media.VOLUME_CHANGED_ACTION");
registerReceiver(volumeReceiver, filter);
// 使用 Glide 加载图片
Glide.with(MainApplication.getContext())
.asDrawable()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.load(soundItemList.getPreUrl())
.placeholder(R.mipmap.default_image)
.error(R.mipmap.default_image)
.transform(new CenterCrop())
.into(binding.image);
}
private BroadcastReceiver volumeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.media.VOLUME_CHANGED_ACTION")) {
// 获取当前音量值并更新SeekBar的进度
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
binding.progress.setProgress(currentVolume);
}
}
};
private void checkFavoriteStatus() {
new Thread(new Runnable() {
@Override
public void run() {
// 检查数据库中是否存在该音频的收藏记录
FavoriteSounds favoriteSound = favoriteSoundsDao.getByPath(soundItemList.getMp3Url());
isFavorite = favoriteSound != null;
runOnUiThread(new Runnable() {
@Override
public void run() {
updateFavoriteIcon();
}
});
}
}).start();
}
private void updateFavoriteIcon() {
binding.favorite.setImageResource(isFavorite ? R.drawable.favorite : R.drawable.unfavorite);
}
private void updateDatabase() {
new Thread(new Runnable() {
@Override
public void run() {
// 判断是否收藏
if (isFavorite) {
// 插入数据到数据库
FavoriteSounds favoriteSound = new FavoriteSounds(
soundItemList.getTitle(),
soundItemList.getMp3Url(),
soundItemList.getPreUrl()
);
favoriteSoundsDao.insertSound(favoriteSound);
} else {
// 从数据库删除数据先根据路径检索出要删除的对象
FavoriteSounds favoriteSound = favoriteSoundsDao.getByPath(soundItemList.getMp3Url());
if (favoriteSound != null) {
favoriteSoundsDao.delete(favoriteSound);
}
}
}
}).start();
}
private void playAudio() {
String audioUrl = soundItemList.getMp3Url(); // 获取音频的URL
if (mediaPlayer == null) {
// 如果MediaPlayer对象为空则创建一个新的MediaPlayer对象
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流类型为音乐
try {
if (audioUrl.startsWith("content://")) {
// 如果音频URL是content://格式使用URI方式设置数据源
Uri audioUri = Uri.parse(audioUrl);
mediaPlayer.setDataSource(this, audioUri);
} else {
// 普通URL格式使用字符串方式设置数据源
mediaPlayer.setDataSource(audioUrl);
}
mediaPlayer.prepareAsync(); // 异步准备MediaPlayer
// 设置MediaPlayer准备完成监听器
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start(); // 开始播放音频
binding.playback.setImageResource(R.drawable.pause); // 播放时显示暂停图标
isPlaying = true; // 设置播放状态为正在播放
mediaPlayer.setLooping(isLooping); // 设置是否循环播放
}
});
// 设置MediaPlayer错误监听器
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Toast.makeText(PlaySoundActivity.this, "Error playing audio", Toast.LENGTH_SHORT).show(); // 显示错误提示
return false;
}
});
// 设置MediaPlayer播放完成监听器
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if (!isLooping) {
binding.playback.setImageResource(R.drawable.playback); // 播放完成时显示播放图标
isPlaying = false; // 设置播放状态为未播放
}
}
});
} catch (IOException e) {
e.printStackTrace(); // 打印异常堆栈信息
Toast.makeText(PlaySoundActivity.this, "Error setting data source", Toast.LENGTH_SHORT).show(); // 显示错误提示
}
} else {
// 如果MediaPlayer对象已经存在
if (isPlaying) {
mediaPlayer.pause(); // 暂停播放
binding.playback.setImageResource(R.drawable.playback); // 暂停时显示播放图标
} else {
mediaPlayer.start(); // 继续播放
binding.playback.setImageResource(R.drawable.pause); // 播放时显示暂停图标
}
isPlaying = !isPlaying; // 切换播放状态
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销广播接收器
unregisterReceiver(volumeReceiver);
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
}

View File

@ -0,0 +1,32 @@
package com.sound.prankparty.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.sound.prankparty.R;
public class PrivacyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_privacy);
WebView webView = findViewById(R.id.webview);
ImageView back = findViewById(R.id.btn_back);
// 设置WebView的客户端
webView.setWebViewClient(new WebViewClient());
back.setOnClickListener(v -> finish());
webView.loadUrl("file:///android_asset/privacy.html");
}
}

View File

@ -0,0 +1,207 @@
package com.sound.prankparty.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.sound.prankparty.R;
import com.sound.prankparty.Room.AppDatabase;
import com.sound.prankparty.Room.SaveSounds;
import com.sound.prankparty.Room.SaveSoundsDao;
import com.sound.prankparty.databinding.ActivitySaveRecordBinding;
import java.io.IOException;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class SaveRecordActivity extends AppCompatActivity {
private ActivitySaveRecordBinding binding;
private String audioFilePath;
private String inputText;
private MediaPlayer mediaPlayer;
private String datePart;
private String timePart;
private boolean isPlaying = false;
private AppDatabase appDatabase;
private SaveSoundsDao saveSoundsDao;
private int duration;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivitySaveRecordBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// 获取传递过来的录音文件路径
audioFilePath = getIntent().getStringExtra("audioFilePath");
if (audioFilePath == null) {
Toast.makeText(this, "音频文件路径无效", Toast.LENGTH_SHORT).show();
return;
}
Log.d("SaveRecordActivity", audioFilePath);
// 初始化数据库和 DAO
appDatabase = AppDatabase.getDatabase(this);
saveSoundsDao = appDatabase.saveSoundsDao();
// 创建一个 SpannableString 并设置它的大小
SpannableString hint = new SpannableString(getString(R.string.give_name));
hint.setSpan(new AbsoluteSizeSpan(15, true), 0, hint.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 设置字体大小为 20sp
binding.saveRecordName.setHint(hint);
// 设置焦点变化监听器
binding.saveRecordName.setOnFocusChangeListener((v, hasFocus) -> binding.saveRecordName.setCursorVisible(hasFocus));
binding.saveRecordBack.setOnClickListener(v -> finish());
// 处理全局点击事件
binding.main.setOnClickListener(v -> {
if (v instanceof ViewGroup) {
clearFocusFromAllChildren((ViewGroup) v);
} else {
clearFocus();
}
inputText = binding.saveRecordName.getText().toString();
Log.d("lux231", "13" + inputText);
});
// 显示音频文件的时长
displayAudioDuration();
binding.saveRecordPlay.setOnClickListener(v -> playOrPauseAudio());
binding.saveRecordSave.setOnClickListener(v -> {
inputText = binding.saveRecordName.getText().toString();
if (inputText == null || inputText.trim().isEmpty()) {
Toast.makeText(getApplicationContext(), "名称不能为空", Toast.LENGTH_SHORT).show();
return;
}
formatDateTime();
new Thread(() -> {
saveSoundsDao.insertSound(new SaveSounds(inputText, audioFilePath, datePart, timePart, formatTime(duration)));
runOnUiThread(() -> {
Intent intent = new Intent(SaveRecordActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
});
}).start();
});
}
private void clearFocusFromAllChildren(ViewGroup parent) {
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
if (child instanceof ViewGroup) {
clearFocusFromAllChildren((ViewGroup) child);
} else {
child.clearFocus();
}
}
}
private void clearFocus() {
binding.saveRecordName.clearFocus();
}
private void displayAudioDuration() {
if (audioFilePath != null) {
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(audioFilePath);
mediaPlayer.prepare();
duration = mediaPlayer.getDuration();
binding.saveRecordTime.setText(formatTime(duration));
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "无法加载音频", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(this, "音频文件路径无效", Toast.LENGTH_SHORT).show();
}
}
private void playOrPauseAudio() {
if (isPlaying) {
mediaPlayer.pause();
binding.saveRecordPlay.setImageResource(R.drawable.playback); // 更新按钮为播放图标
isPlaying = false;
} else {
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(audioFilePath);
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "无法播放音频", Toast.LENGTH_SHORT).show();
return;
}
}
mediaPlayer.start();
binding.saveRecordPlay.setImageResource(R.drawable.pause); // 更新按钮为暂停图标
isPlaying = true;
mediaPlayer.setOnCompletionListener(mp -> {
Toast.makeText(this, "播放完成", Toast.LENGTH_SHORT).show();
binding.saveRecordPlay.setImageResource(R.drawable.playback); // 恢复播放图标
isPlaying = false;
});
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
if (isPlaying) {
mediaPlayer.stop();
}
mediaPlayer.release();
mediaPlayer = null;
}
}
private String formatTime(int duration) {
int seconds = (duration / 1000) % 60;
int minutes = (duration / (1000 * 60)) % 60;
int hours = (duration / (1000 * 60 * 60)) % 24;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
private void formatDateTime() {
Date currentDate = new Date();
// 自定义DateFormatSymbols以使用小写的am和pm
DateFormatSymbols dfs = new DateFormatSymbols(Locale.ENGLISH);
dfs.setAmPmStrings(new String[]{"am", "pm"});
// 新的格式
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH);
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mma", Locale.ENGLISH);
timeFormat.setDateFormatSymbols(dfs);
// 赋值给原有变量名
datePart = dateFormat.format(currentDate);
timePart = timeFormat.format(currentDate);
}
}

View File

@ -0,0 +1,47 @@
package com.sound.prankparty.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.ProgressBar;
import androidx.appcompat.app.AppCompatActivity;
import com.sound.prankparty.databinding.ActivitySplashBinding;
public class SplashActivity extends AppCompatActivity {
private static final int SPLASH_TIME_OUT = 5000;
private ActivitySplashBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivitySplashBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// 使用 ViewBinding 引用 progressBar
ProgressBar progressBar = binding.progressBar;
new Handler(Looper.getMainLooper()).postDelayed(() -> {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}, SPLASH_TIME_OUT);
// 进度条进展
new Thread(() -> {
for (int i = 0; i <= 100; i++) {
final int progress = i;
runOnUiThread(() -> progressBar.setProgress(progress));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}

View File

@ -0,0 +1,106 @@
package com.sound.prankparty.Adapter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import com.sound.MainApplication;
import com.sound.prankparty.Activity.PlaySoundActivity;
import com.sound.prankparty.JSON.SoundItem;
import com.sound.prankparty.R;
import java.util.List;
public class AirHornRecyclerViewAdapter extends RecyclerView.Adapter<AirHornRecyclerViewAdapter.AirhonrViewHolder> {
private Context context;
private List<SoundItem> soundItemList;
private int color;
public AirHornRecyclerViewAdapter(Context context, List<SoundItem> soundItemList,int color) {
this.context = context;
this.soundItemList = soundItemList;
this.color = color;
}
@NonNull
@Override
public AirhonrViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_airhorn, parent, false);
return new AirHornRecyclerViewAdapter.AirhonrViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull AirhonrViewHolder holder, int position) {
SoundItem soundItem = soundItemList.get(position);
holder.imageView.setBackgroundColor(color);
String originalName = soundItem.getTitle();
// 检查名称是否为空以避免空指针异常
if (originalName != null && !originalName.isEmpty()) {
// // 将首字母大写
// String capitalizedName = originalName.substring(0, 1).toUpperCase() + originalName.substring(1);
// 设置文本字体
holder.textView.setTypeface(Typeface.createFromAsset(context.getAssets(), "FrancoisOne-Regular.ttf"));
//设置文本
holder.textView.setText(originalName);
} else {
// 如果名称为空可以设置一个默认值或者不设置
holder.textView.setText("");
}
if (soundItem != null) {
Glide.with(MainApplication.getContext())
//指定加载的资源类型为Drawable
.asDrawable()
//设置跳过内存缓存即加载的图片不会存储在内存缓存中
.skipMemoryCache(true)
//设置磁盘缓存策略为DiskCacheStrategy.ALL即缓存原始图片和转换后的图片
.diskCacheStrategy(DiskCacheStrategy.ALL)
.load(soundItem.getPreUrl())
.transform(new CenterCrop())
.into(holder.imageView);
holder.imageView.setOnClickListener(v -> {
Intent intent = new Intent(context, PlaySoundActivity.class);
intent.putExtra("1234", soundItem);
context.startActivity(intent);
});
}
}
@Override
public int getItemCount() {
return soundItemList.size();
}
static class AirhonrViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public AirhonrViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.item_imageview_airhorn);
textView = itemView.findViewById(R.id.item_textview_airhorn);
}
}
}

View File

@ -0,0 +1,111 @@
package com.sound.prankparty.Adapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.sound.MainApplication;
import com.sound.prankparty.Activity.PlaySoundActivity;
import com.sound.prankparty.JSON.SoundItem;
import com.sound.prankparty.R;
import com.sound.prankparty.Room.FavoriteSounds;
import com.sound.prankparty.Utils.FavoriteSoundsDiffCallback;
import java.util.ArrayList;
import java.util.List;
public class FavoriteRecyclerViewAdapter extends RecyclerView.Adapter<FavoriteRecyclerViewAdapter.FavoriteRecyclerViewHolder> {
private List<FavoriteSounds> favoriteSoundsList = new ArrayList<>();
private Context context;
private Fragment fragment;
private final int[] colors;
public FavoriteRecyclerViewAdapter(Context context, Fragment fragment) {
this.context = context;
this.fragment = fragment;
colors = new int[]{
ContextCompat.getColor(context, R.color.color2),
ContextCompat.getColor(context, R.color.color3),
ContextCompat.getColor(context, R.color.color4),
ContextCompat.getColor(context, R.color.color5),
ContextCompat.getColor(context, R.color.color6),
ContextCompat.getColor(context, R.color.color7)
// 添加更多颜色
};
}
public void updateData(List<FavoriteSounds> newFavoriteSoundsList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new FavoriteSoundsDiffCallback(favoriteSoundsList, newFavoriteSoundsList));
favoriteSoundsList.clear();
favoriteSoundsList.addAll(newFavoriteSoundsList);
diffResult.dispatchUpdatesTo(this);
}
@NonNull
@Override
public FavoriteRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_favorite, parent, false);
return new FavoriteRecyclerViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull FavoriteRecyclerViewHolder holder, int position) {
FavoriteSounds favoriteSounds = favoriteSoundsList.get(position);
holder.textView.setText(favoriteSounds.getName());
// 使用取模运算来循环使用颜色
holder.imageView.setBackgroundColor(colors[position % colors.length]);
holder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建 SoundItem 对象并传递你想存入的值
SoundItem soundItem = new SoundItem(favoriteSounds.getPath(), favoriteSounds.getImagePath(), favoriteSounds.getName());
Intent intent = new Intent(fragment.getContext(), PlaySoundActivity.class);
intent.putExtra("1234",soundItem);
fragment.startActivity(intent);
}
});
Glide.with(MainApplication.getContext())
.asDrawable()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.load(favoriteSounds.getImagePath())
.placeholder(R.mipmap.default_image)
.error(R.mipmap.default_image)
.transform(new CenterCrop())
.into(holder.imageView);
}
@Override
public int getItemCount() {
return favoriteSoundsList != null ? favoriteSoundsList.size() : 0;
}
static class FavoriteRecyclerViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public FavoriteRecyclerViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.item_imageview_favorite);
textView = itemView.findViewById(R.id.item_imageview_text);
}
}
}

View File

@ -0,0 +1,40 @@
package com.sound.prankparty.Adapter;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.sound.prankparty.Fragment.SoundFragment;
import com.sound.prankparty.Fragment.RadioFragment;
import com.sound.prankparty.Fragment.FavoriteFragment;
import java.util.ArrayList;
import java.util.List;
public class MainViewPager2Adapter extends FragmentStateAdapter {
private List<Fragment> fragmentList;
public MainViewPager2Adapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
// 初始化fragmentList并添加Fragment对象
fragmentList = new ArrayList<>();
fragmentList.add(new SoundFragment());
fragmentList.add(new RadioFragment());
fragmentList.add(new FavoriteFragment());
}
@NonNull
@Override
public Fragment createFragment(int position) {
// 根据位置返回相应的Fragment
return fragmentList.get(position);
}
@Override
public int getItemCount() {
// 返回Fragment列表的大小
return fragmentList.size();
}
}

View File

@ -0,0 +1,134 @@
package com.sound.prankparty.Adapter;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import com.sound.prankparty.Activity.PlaySoundActivity;
import com.sound.prankparty.JSON.SoundItem;
import com.sound.prankparty.R;
import com.sound.prankparty.Room.FavoriteSounds;
import com.sound.prankparty.Room.FavoriteSoundsDao;
import com.sound.prankparty.Room.SaveSounds;
import com.sound.prankparty.Room.SaveSoundsDao;
import java.util.List;
import java.util.concurrent.Executors;
public class RadioRecyclerViewAdapter extends RecyclerView.Adapter<RadioRecyclerViewAdapter.RadioViewHolder> {
private List<SaveSounds> saveSoundsList;
private Context context;
private Fragment fragment;
private FavoriteSoundsDao favoriteSoundsDao;
private SaveSoundsDao saveSoundsDao;
public RadioRecyclerViewAdapter(Context context, Fragment fragment, FavoriteSoundsDao favoriteSoundsDao, SaveSoundsDao saveSoundsDao) {
this.context = context;
this.fragment = fragment;
this.favoriteSoundsDao = favoriteSoundsDao;
this.saveSoundsDao = saveSoundsDao;
}
@NonNull
@Override
public RadioViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_radio, parent, false);
return new RadioViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RadioViewHolder holder, int position) {
SaveSounds saveSound = saveSoundsList.get(position);
holder.title.setText(saveSound.getName());
holder.date.setText(saveSound.getDate());
holder.time.setText(saveSound.getTime());
holder.duration.setText(saveSound.getDuration());
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建 SoundItem 对象并传递你想存入的值
SoundItem soundItem = new SoundItem(saveSound.getPath(), "", saveSound.getName());
Intent intent = new Intent(fragment.getContext(), PlaySoundActivity.class);
intent.putExtra("1234", soundItem);
fragment.startActivity(intent);
}
});
Executors.newSingleThreadExecutor().execute(() -> {
final boolean[] isFavorite = {checkFavoriteStatus(saveSound)};
// 更新 UI 需要在主线程中执行
fragment.requireActivity().runOnUiThread(() -> {
updateFavoriteIcon(holder, isFavorite[0]);
holder.favorite.setOnClickListener(v -> {
isFavorite[0] = !isFavorite[0];
updateFavoriteIcon(holder, isFavorite[0]);
updateDatabase(saveSound, isFavorite[0]);
});
});
});
}
@Override
public int getItemCount() {
return saveSoundsList != null ? saveSoundsList.size() : 0;
}
public void updateData(List<SaveSounds> newSaveSoundsList) {
this.saveSoundsList = newSaveSoundsList;
notifyDataSetChanged();
}
private boolean checkFavoriteStatus(SaveSounds saveSound) {
FavoriteSounds favoriteSound = favoriteSoundsDao.getByPath(saveSound.getPath());
return favoriteSound != null;
}
private void updateDatabase(SaveSounds saveSound, boolean isFavorite) {
Executors.newSingleThreadExecutor().execute(() -> {
if (isFavorite) {
favoriteSoundsDao.insertSound(new FavoriteSounds(saveSound.getName(), saveSound.getPath(), ""));
} else {
favoriteSoundsDao.delete(favoriteSoundsDao.getByPath(saveSound.getPath()));
}
});
}
private void updateFavoriteIcon(RadioViewHolder holder, boolean isFavorite) {
holder.favorite.setImageResource(isFavorite ? R.drawable.favorite : R.drawable.unfavorite);
}
static class RadioViewHolder extends RecyclerView.ViewHolder {
private TextView title;
private TextView date;
private TextView time;
private TextView duration;
private ImageView favorite;
private ConstraintLayout root;
public RadioViewHolder(@NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title);
date = itemView.findViewById(R.id.date);
time = itemView.findViewById(R.id.time);
duration = itemView.findViewById(R.id.duration);
favorite = itemView.findViewById(R.id.favorite);
root = itemView.findViewById(R.id.item_con);
}
}
}

View File

@ -0,0 +1,122 @@
package com.sound.prankparty.Adapter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import com.sound.prankparty.Activity.AirHornActivity;
import com.sound.MainApplication;
import com.sound.prankparty.JSON.Category;
import com.sound.prankparty.R;
import java.util.List;
public class SoundRecyclerViewAdapter extends RecyclerView.Adapter<SoundRecyclerViewAdapter.SoundViewHolder> {
private Context context;
private List<Category> categoryList;
private final int[] colors;
public SoundRecyclerViewAdapter(Context context, List<Category> categoryList) {
this.context = context;
this.categoryList = categoryList;
colors = new int[]{
ContextCompat.getColor(context, R.color.color2),
ContextCompat.getColor(context, R.color.color3),
ContextCompat.getColor(context, R.color.color4),
ContextCompat.getColor(context, R.color.color5),
ContextCompat.getColor(context, R.color.color6),
ContextCompat.getColor(context, R.color.color7)
// 添加更多颜色
};
}
@NonNull
@Override
public SoundViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_sound, parent, false);
return new SoundViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull SoundViewHolder holder, int position) {
Category category = categoryList.get(position);
// 使用取模运算来循环使用颜色
holder.imageView.setBackgroundColor(colors[position % colors.length]);
// 假设 `colors[position % colors.length]` 是你要传递的颜色值
int currentColor = colors[position % colors.length];
String originalName = category.getCategoryName();
// 检查名称是否为空以避免空指针异常
if (originalName != null && !originalName.isEmpty()) {
// // 将首字母大写
// String capitalizedName = originalName.substring(0, 1).toUpperCase() + originalName.substring(1);
// 设置文本字体
holder.textView.setTypeface(Typeface.createFromAsset(context.getAssets(), "FrancoisOne-Regular.ttf"));
//设置文本
holder.textView.setText(originalName);
} else {
// 如果名称为空可以设置一个默认值或者不设置
holder.textView.setText("");
}
if (category != null) {
Glide.with(MainApplication.getContext())
//指定加载的资源类型为Drawable
.asDrawable()
//设置跳过内存缓存即加载的图片不会存储在内存缓存中
.skipMemoryCache(true)
//设置磁盘缓存策略为DiskCacheStrategy.ALL即缓存原始图片和转换后的图片
.diskCacheStrategy(DiskCacheStrategy.ALL)
.load(category.getCategoryUrl())
.transform(new CenterCrop())
.into(holder.imageView);
holder.imageView.setOnClickListener(v -> {
Intent intent = new Intent(context, AirHornActivity.class);
intent.putExtra("color", currentColor); // 添加颜色值到 Intent
intent.putExtra("123",category);
context.startActivity(intent);
});
}
}
@Override
public int getItemCount() {
return categoryList.size();
}
static class SoundViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public SoundViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.item_imageview_sound);
textView = itemView.findViewById(R.id.item_textview_sound);
}
}
}

View File

@ -0,0 +1,78 @@
package com.sound.prankparty.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import com.sound.prankparty.R;
import com.sound.prankparty.Room.AppDatabase;
import com.sound.prankparty.Room.FavoriteSoundsDao;
import com.sound.prankparty.Adapter.FavoriteRecyclerViewAdapter;
import com.sound.prankparty.Utils.FavoriteSoundsViewModel;
import com.sound.prankparty.Utils.ItemDecoration;
import com.sound.prankparty.databinding.FragmentFavoriteBinding;
public class FavoriteFragment extends Fragment {
private AppDatabase appDatabase;
private FavoriteSoundsDao favoriteSoundsDao;
private FavoriteRecyclerViewAdapter favoriteRecyclerViewAdapter;
private FavoriteSoundsViewModel viewModel;
private FragmentFavoriteBinding binding;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentFavoriteBinding.inflate(inflater, container, false);
View view = binding.getRoot();
// 使用 ApplicationContext 来获取 ViewModel
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(requireActivity().getApplication())).get(FavoriteSoundsViewModel.class);
binding.favoriteRecyclerview.setLayoutManager(new GridLayoutManager(getContext(), 3));
favoriteRecyclerViewAdapter = new FavoriteRecyclerViewAdapter(requireContext(), this);
binding.favoriteRecyclerview.setAdapter(favoriteRecyclerViewAdapter);
// RecyclerView 添加自定义的间距装饰
ItemDecoration itemDecoration = new ItemDecoration(16, 19, 10);
binding.favoriteRecyclerview.addItemDecoration(itemDecoration);
appDatabase = AppDatabase.getDatabase(requireContext());
favoriteSoundsDao = appDatabase.favoriteSoundsDao();
// 观察 LiveData 的变化
viewModel.getFavoriteSoundsLiveData().observe(getViewLifecycleOwner(), favoriteSoundsList -> {
if (favoriteSoundsList == null || favoriteSoundsList.isEmpty()) {
binding.favoriteRecyclerview.setVisibility(View.GONE);
binding.fragmentFavoriteTips.setVisibility(View.VISIBLE);
binding.fragmentFavoriteImage.setVisibility(View.VISIBLE);
} else {
binding.favoriteRecyclerview.setVisibility(View.VISIBLE);
binding.fragmentFavoriteTips.setVisibility(View.GONE);
binding.fragmentFavoriteImage.setVisibility(View.GONE);
}
favoriteRecyclerViewAdapter.updateData(favoriteSoundsList);
});
return view;
}
@Override
public void onResume() {
super.onResume();
// 触发数据刷新
viewModel.refreshData();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@ -0,0 +1,333 @@
package com.sound.prankparty.Fragment;
import static android.app.Activity.RESULT_OK;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.sound.prankparty.Activity.NewRecordActivity;
import com.sound.prankparty.Adapter.RadioRecyclerViewAdapter;
import com.sound.prankparty.R;
import com.sound.prankparty.Room.AppDatabase;
import com.sound.prankparty.Room.FavoriteSoundsDao;
import com.sound.prankparty.Room.SaveSounds;
import com.sound.prankparty.Room.SaveSoundsDao;
import com.sound.prankparty.Utils.MyItemTouchHelperCallback;
import com.sound.prankparty.databinding.FragmentRadioBinding;
import java.io.File;
import java.io.IOException;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
public class RadioFragment extends Fragment {
private FragmentRadioBinding binding;
private RadioRecyclerViewAdapter radioRecyclerViewAdapter;
private SaveSoundsDao saveSoundsDao;
private MyItemTouchHelperCallback myItemTouchHelperCallback;
private ItemTouchHelper itemTouchHelper;
private Uri audioUri;
private String fileName;
private AppDatabase appDatabase;
private FavoriteSoundsDao favoriteSoundsDao;
private List<SaveSounds> saveSoundsList;
private static final int REQUEST_CODE_READ_MEDIA_AUDIO = 1001;
private static final int REQUEST_CODE_PICK_AUDIO = 1002;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// 使用 ViewBinding 初始化视图
binding = FragmentRadioBinding.inflate(inflater, container, false);
View view = binding.getRoot();
// 初始化数据库
appDatabase = AppDatabase.getDatabase(requireContext());
saveSoundsDao = appDatabase.saveSoundsDao();
favoriteSoundsDao = appDatabase.favoriteSoundsDao();
// 初始化 RecyclerView
binding.radioRecyclerview.setLayoutManager(new GridLayoutManager(getContext(), 1));
radioRecyclerViewAdapter = new RadioRecyclerViewAdapter(requireContext(), this, favoriteSoundsDao,saveSoundsDao);
binding.radioRecyclerview.setAdapter(radioRecyclerViewAdapter);
// 初始化 MyItemTouchHelperCallback
myItemTouchHelperCallback = new MyItemTouchHelperCallback(this);
itemTouchHelper = new ItemTouchHelper(myItemTouchHelperCallback);
itemTouchHelper.attachToRecyclerView(binding.radioRecyclerview);
// 设置添加按钮的点击事件
binding.fragmentRadioAdd.setOnClickListener(v -> showCustomBottomSheetDialog());
// 刷新数据
refreshData();
return view;
}
@Override
public void onResume() {
super.onResume();
refreshData();
}
public void deleteItem(int position) {
new Thread(() -> {
SaveSounds sound = saveSoundsList.get(position);
saveSoundsDao.deleteSound(sound);
requireActivity().runOnUiThread(() -> {
refreshData();
});
}).start();
}
private void showCustomBottomSheetDialog() {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(requireContext());
View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_dialog, null);
bottomSheetDialog.setCanceledOnTouchOutside(false);
// 设置选项1的点击事件
dialogView.findViewById(R.id.option1).setOnClickListener(v -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
openAudioPicker();
} else {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_CODE_READ_MEDIA_AUDIO);
} else {
openAudioPicker();
}
}
bottomSheetDialog.dismiss();
});
// 设置选项2的点击事件
dialogView.findViewById(R.id.option2).setOnClickListener(v -> {
Intent intent = new Intent(getActivity(), NewRecordActivity.class);
startActivity(intent);
bottomSheetDialog.dismiss();
});
// 设置取消按钮的点击事件
dialogView.findViewById(R.id.cancel_button).setOnClickListener(v -> {
bottomSheetDialog.dismiss();
});
bottomSheetDialog.setContentView(dialogView);
bottomSheetDialog.show();
}
// 处理权限请求结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_READ_MEDIA_AUDIO) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openAudioPicker();
} else {
Toast.makeText(requireContext(), "Permission denied", Toast.LENGTH_SHORT).show();
}
}
}
// 打开音频选择器
private void openAudioPicker() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); // 改为 ACTION_OPEN_DOCUMENT
intent.setType("audio/*"); // 设置 MIME 类型为音频
intent.addCategory(Intent.CATEGORY_OPENABLE); // 限制为可打开的文件
startActivityForResult(intent, REQUEST_CODE_PICK_AUDIO);
}
// 处理 Activity 结果
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_PICK_AUDIO && resultCode == RESULT_OK) {
if (data != null) {
audioUri = data.getData();
assert audioUri != null;
fileName = getFileName(audioUri);
long duration = 0;
try {
duration = getAudioDuration(audioUri);
} catch (IOException e) {
throw new RuntimeException(e);
}
// 获取文件的最后修改时间
long lastModifiedTime = getLastModifiedTime(requireContext(),audioUri);
String[] lastModifiedDateTime = getDateTimeFromLastModified(lastModifiedTime);
long finalDuration = duration;
new Thread(() -> {
try {
saveSoundsDao.insertSound(new SaveSounds(fileName, audioUri.toString(), lastModifiedDateTime[0], lastModifiedDateTime[1], formatDuration(finalDuration)));
} catch (Exception e) {
e.printStackTrace();
// 在主线程中显示错误信息
requireActivity().runOnUiThread(() ->
Toast.makeText(requireContext(), "插入数据库失败: " + e.getMessage(), Toast.LENGTH_LONG).show());
}
}).start();
}
}
}
// 获取文件名
private String getFileName(Uri uri) {
String result = null;
if (Objects.equals(uri.getScheme(), "content")) {
try (Cursor cursor = requireActivity().getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int displayNameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
if (displayNameIndex != -1) {
result = cursor.getString(displayNameIndex);
}
}
}
}
if (result == null) {
result = uri.getPath();
assert result != null;
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
// 获取音频时长
private long getAudioDuration(Uri uri) throws IOException {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(requireContext(), uri);
String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
return duration != null ? Long.parseLong(duration) : 0;
} catch (Exception e) {
e.printStackTrace(); // 可以记录日志或进行其他异常处理
return 0;
} finally {
retriever.release();
}
}
// 格式化时长
private String formatDuration(long duration) {
long minutes = (duration / 1000) / 60;
long seconds = (duration / 1000) % 60;
return String.format(Locale.getDefault(), "%d:%02d", minutes, seconds);
}
//获取最后修改时间
public long getLastModifiedTime(Context context, Uri uri) {
long lastModifiedTime = 0;
if (uri != null && "content".equals(uri.getScheme())) {
DocumentFile documentFile = DocumentFile.fromSingleUri(context, uri);
if (documentFile != null && documentFile.exists()) {
lastModifiedTime = documentFile.lastModified();
}
} else if (uri != null && "file".equals(uri.getScheme())) {
File file = new File(uri.getPath());
if (file.exists()) {
lastModifiedTime = file.lastModified();
}
}
return lastModifiedTime;
}
public static String[] getDateTimeFromLastModified(long lastModifiedTime) {
// 自定义DateFormatSymbols以使用小写的am和pm
DateFormatSymbols dfs = new DateFormatSymbols(Locale.ENGLISH);
dfs.setAmPmStrings(new String[]{"am", "pm"});
// 使用自定义的DateFormatSymbols创建SimpleDateFormat
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH);
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mma", Locale.ENGLISH);
timeFormat.setDateFormatSymbols(dfs);
// 将最后修改时间转换为Date对象
Date lastModifiedDate = new Date(lastModifiedTime);
String date = dateFormat.format(lastModifiedDate);
String time = timeFormat.format(lastModifiedDate);
// 返回日期和时间字符串
return new String[]{date, time};
}
// 刷新数据
public void refreshData() {
new Thread(() -> {
saveSoundsList = saveSoundsDao.getAllSounds();
requireActivity().runOnUiThread(() -> {
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.fragmentRadioAdd.getLayoutParams();
if (saveSoundsList.isEmpty()) {
binding.radioRecyclerview.setVisibility(View.GONE);
binding.emptyRadioImage.setVisibility(View.VISIBLE);
// 清除与底部对齐的约束
layoutParams.topToBottom = ConstraintLayout.LayoutParams.UNSET;
layoutParams.bottomToTop = ConstraintLayout.LayoutParams.UNSET;
// add 移动到屏幕中央
layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
} else {
binding.radioRecyclerview.setVisibility(View.VISIBLE);
binding.emptyRadioImage.setVisibility(View.GONE);
// 清除与中央对齐的约束
layoutParams.topToTop = ConstraintLayout.LayoutParams.UNSET;
layoutParams.bottomToTop = ConstraintLayout.LayoutParams.UNSET;
// add 移动到屏幕底部
layoutParams.topToBottom = R.id.radio_recyclerview;
layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
}
binding.fragmentRadioAdd.setLayoutParams(layoutParams);
radioRecyclerViewAdapter.updateData(saveSoundsList);
});
}).start();
}
}

View File

@ -0,0 +1,124 @@
package com.sound.prankparty.Fragment;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.sound.MainApplication;
import com.sound.prankparty.Activity.PrivacyActivity;
import com.sound.prankparty.Adapter.SoundRecyclerViewAdapter;
import com.sound.prankparty.JSON.Category;
import com.sound.prankparty.R;
import com.sound.prankparty.Utils.ItemDecoration;
import com.sound.prankparty.Utils.JsonParser;
import com.sound.prankparty.Utils.LoadJSON;
import com.sound.prankparty.databinding.FragmentSoundBinding;
import java.util.List;
public class SoundFragment extends Fragment {
private FragmentSoundBinding binding;
private PackageInfo packageInfo;
private String format;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 使用 ViewBinding 初始化视图
binding = FragmentSoundBinding.inflate(inflater, container, false);
View view = binding.getRoot();
// 设置 RecyclerView 的布局管理器和适配器
binding.soundRecyclerview.setLayoutManager(new GridLayoutManager(getContext(), 2));
// assets 文件夹中读取 JSON 文件
String jsonString = LoadJSON.loadJSONFromAsset("prank.json");
// 解析读取的 String
List<Category> categories = JsonParser.parseJson(jsonString);
binding.soundRecyclerview.setAdapter(new SoundRecyclerViewAdapter(getContext(), categories));
// RecyclerView 添加自定义的间距装饰
ItemDecoration itemDecoration = new ItemDecoration(16, 19, 10);
binding.soundRecyclerview.addItemDecoration(itemDecoration);
// 设置返回按钮的点击事件
binding.fragmentSoundSetting.setOnClickListener(v -> showCustomBottomSheetDialog());
return view;
}
private void showCustomBottomSheetDialog() {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(requireContext());
View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.setting_dialog, null);
// 禁止点击其他页面取消
bottomSheetDialog.setCanceledOnTouchOutside(false);
TextView version = dialogView.findViewById(R.id.version_number);
//更新版本号
try {
packageInfo = MainApplication.getContext().getPackageManager().getPackageInfo(MainApplication.getContext().getPackageName(), 0);
String versionName = packageInfo.versionName;
format = String.format(getString(R.string.gp), packageInfo.packageName);
version.setText(versionName);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
version.setText("1.0.0");
format = String.format(getString(R.string.gp), "com.sound.prankparty");
}
dialogView.findViewById(R.id.privacy).setOnClickListener(v -> {
// 处理选项 1
Intent intent = new Intent(requireActivity(), PrivacyActivity.class);
startActivity(intent);
// 销毁 BottomSheetDialog 对象
bottomSheetDialog.dismiss();
});
dialogView.findViewById(R.id.share).setOnClickListener(v -> {
shareString(format);
// 处理选项 2
bottomSheetDialog.dismiss();
});
dialogView.findViewById(R.id.version).setOnClickListener(v -> {
});
dialogView.findViewById(R.id.cancel_button).setOnClickListener(v -> {
// 处理取消按钮
bottomSheetDialog.dismiss();
});
bottomSheetDialog.setContentView(dialogView);
bottomSheetDialog.show();
}
/**
* 分享纯文本
*
* @param content 内容
*/
public void shareString(String content) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, content);//分享的文本内容
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, "Share to"));
}
}

View File

@ -18,11 +18,23 @@ public class SoundItem implements Serializable {
return mp3Url;
}
public void setMp3Url(String mp3Url) {
this.mp3Url = mp3Url;
}
public String getPreUrl() {
return preUrl;
}
public void setPreUrl(String preUrl) {
this.preUrl = preUrl;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@ -9,7 +9,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.sound.prankparty.Application.MainApplication;
import com.sound.MainApplication;
// ItemDecoration 是一个自定义的 RecyclerView.ItemDecoration 用于在 RecyclerView 中为每个子项设置间距

View File

@ -1,6 +1,6 @@
package com.sound.prankparty.Utils;
import com.sound.prankparty.Application.MainApplication;
import com.sound.MainApplication;
import java.io.BufferedReader;
import java.io.InputStreamReader;

View File

@ -1,62 +1,38 @@
package com.sound.prankparty.Utils;
import android.content.Context;
import android.graphics.Canvas;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.sound.prankparty.Fragment.RadioFragment;
import com.sound.prankparty.R;
public class MyItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
private float maxDistance; // 最大滑动距离
private boolean isMaxSwiped = false; // 是否达到最大距离
private RecyclerView.ViewHolder swipedViewHolder; // 被滑动的ViewHolder
private RadioFragment radioFragment;
public MyItemTouchHelperCallback() {
// 构造方法初始化回调对象
public MyItemTouchHelperCallback(RadioFragment fragment) {
super(0, ItemTouchHelper.LEFT); // 只允许左滑
this.radioFragment = fragment;
}
// 处理移动事件的方法这里不处理移动事件
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false; // 不处理移动事件
}
// 处理滑动事件的方法
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// 当滑动项被完全滑出时可以在这里处理
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
// 当滑动项被完全滑出时执行相应操作比如删除或编辑
int position = viewHolder.getAdapterPosition();
radioFragment.deleteItem(position);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
// 限制最大滑动距离
maxDistance = recyclerView.getWidth() * 0.5f; // 最大滑动为RecyclerView宽度的50%
if (dX < -maxDistance) {
dX = -maxDistance;
isMaxSwiped = true;
swipedViewHolder = viewHolder;
} else {
isMaxSwiped = false;
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
// 阻止自动滑出始终返回1.0f
return 1.0f;
}
// 复原滑动项的方法
public void restoreSwipedItem() {
if (swipedViewHolder != null) {
View itemView = swipedViewHolder.itemView;
itemView.animate().translationX(0).setDuration(200).start();
swipedViewHolder = null;
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M8.699,15.061C9.066,15.061 9.365,14.764 9.365,14.394L8.699,7.746C8.699,7.379 8.402,7.08 8.033,7.08C7.664,7.08 7.367,7.377 7.367,7.746L8.033,14.394C8.035,14.762 8.332,15.061 8.699,15.061ZM16.678,3.756H12.025V3.424C12.025,2.689 11.43,2.094 10.695,2.094H9.365C8.631,2.094 8.035,2.689 8.035,3.424V3.756H3.381C3.014,3.756 2.715,4.053 2.715,4.422C2.715,4.789 3.012,5.088 3.381,5.088H16.68C17.047,5.088 17.346,4.791 17.346,4.422C17.346,4.053 17.047,3.756 16.678,3.756ZM10.695,3.756H9.365V3.424H10.695V3.756ZM15.418,5.762C15.031,5.762 14.715,6.066 14.697,6.449L13.223,16.391H6.842L5.361,6.418H5.354C5.318,6.051 5.01,5.762 4.637,5.762C4.26,5.762 3.951,6.049 3.92,6.418H3.895L5.258,16.783C5.264,16.729 5.26,16.668 5.248,16.602C5.348,17.236 5.898,17.721 6.561,17.721H13.518C14.184,17.721 14.734,17.232 14.83,16.598C14.822,16.641 14.82,16.68 14.82,16.721L16.172,6.416H16.137C16.1,6.049 15.793,5.762 15.418,5.762ZM12.025,14.394L12.691,7.746C12.691,7.379 12.394,7.08 12.025,7.08C11.656,7.08 11.359,7.377 11.359,7.746L10.693,14.394C10.693,14.762 10.99,15.061 11.359,15.061C11.727,15.061 12.025,14.762 12.025,14.394Z"
android:fillColor="#FF6565"/>
</vector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="0"
android:endColor="#80FFFFFF"
android:startColor="#E6FFFFFF"/>
</shape>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 边框 -->
<item>
<shape android:shape="rectangle">
<corners android:radius="16dp" /> <!-- 圆角半径与item_round一致 -->
<stroke
android:width="0.1dp"
android:color="#000000" /> <!-- 边框颜色为黑色 -->
</shape>
</item>
<!-- 背景 -->
<item
android:bottom="0dp"
android:left="0dp"
android:right="0dp"
android:top="0dp">
<shape android:shape="rectangle">
<corners android:radius="16dp" /> <!-- 圆角半径与item_round一致 -->
<solid android:color="@android:color/transparent" /> <!-- 背景透明 -->
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="16dp" /> <!-- 圆角半径 -->
<solid android:color="#FFFFFF" /> <!-- 背景颜色 -->
</shape>
</item>
</selector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="112dp"
android:height="24dp"
android:viewportWidth="112"
android:viewportHeight="24">
<path
android:pathData="M12.73,19.14V5.63L9.37,19H7.21L3.58,5.58V18.86L0.68,19V1.17L5.84,1.02L8.82,12.16L11.67,1.43L16.38,1.14V18.86L12.73,19.14ZM19.83,23.27C19.45,23.27 18.97,23.22 18.41,23.13V20.32C19.01,20.35 19.48,20.37 19.83,20.37C21.24,20.37 21.94,19.71 21.94,18.4V17.73L18.08,5.66L21.73,5.49L23.62,13.96L25.42,5.56L28.47,5.54L25.01,18.76C24.53,20.49 23.83,21.67 22.9,22.31C21.99,22.95 20.97,23.27 19.83,23.27ZM36.6,8.03H34.94L35.06,5.49H36.6V5.25C36.6,4.3 36.65,3.54 36.76,2.97C36.89,2.38 37.2,1.88 37.68,1.48C38.17,1.08 38.93,0.88 39.96,0.88C40.93,0.88 41.69,1 42.24,1.24V3.26H41.49C40.85,3.26 40.44,3.43 40.24,3.78C40.07,4.14 39.98,4.62 39.98,5.25V5.49H42.36L42.21,8.03H39.98V18.86L36.6,19.14V8.03ZM46.27,19.31C45.69,19.31 45.17,19.14 44.71,18.78C44.24,18.42 43.88,17.93 43.63,17.32C43.37,16.7 43.24,16 43.24,15.23C43.24,13.49 43.83,12.26 45.02,11.56C46.22,10.84 47.61,10.43 49.19,10.34C49.19,9.47 49.14,8.83 49.03,8.42C48.91,8 48.64,7.79 48.21,7.79C47.84,7.79 47.56,7.94 47.37,8.22C47.18,8.51 47.06,8.88 47.01,9.33L43.72,8.75C43.88,7.52 44.39,6.64 45.23,6.11C46.08,5.58 47.07,5.32 48.21,5.32C49.67,5.32 50.75,5.7 51.47,6.47C52.21,7.24 52.58,8.24 52.58,9.47V16.46C52.58,17.21 52.7,18.01 52.94,18.86L49.65,19.14L49.27,17.49C48.98,18 48.55,18.43 47.99,18.78C47.43,19.14 46.86,19.31 46.27,19.31ZM47.83,16.91C48.1,16.91 48.32,16.85 48.5,16.72C48.69,16.59 48.92,16.38 49.19,16.1V12.35C48.39,12.35 47.75,12.57 47.27,13C46.79,13.43 46.55,14.1 46.55,15.02C46.55,15.62 46.66,16.1 46.87,16.43C47.09,16.75 47.41,16.91 47.83,16.91ZM57.75,19L53.99,5.78L58.02,5.49L59.72,14.13L61.67,5.92L64.71,5.63L60.85,19H57.75ZM70.67,19.31C69.05,19.31 67.8,18.67 66.92,17.39C66.06,16.11 65.63,14.41 65.63,12.28C65.63,10.17 66.06,8.48 66.92,7.22C67.79,5.95 69.04,5.32 70.67,5.32C72.28,5.32 73.53,5.95 74.41,7.22C75.31,8.48 75.76,10.17 75.76,12.28C75.76,14.39 75.31,16.1 74.41,17.39C73.53,18.67 72.28,19.31 70.67,19.31ZM70.67,16.82C71.39,16.82 71.85,16.38 72.06,15.5C72.27,14.62 72.37,13.54 72.37,12.26C72.37,10.98 72.27,9.9 72.06,9.02C71.85,8.14 71.39,7.7 70.67,7.7C69.95,7.7 69.49,8.11 69.3,8.94C69.11,9.78 69.01,10.79 69.01,11.99C69.01,13.46 69.11,14.64 69.3,15.52C69.49,16.38 69.95,16.82 70.67,16.82ZM78.04,5.78L81.4,5.49V7.67C81.88,6.98 82.47,6.43 83.18,6.02C83.9,5.58 84.81,5.37 85.91,5.37V8.66C85.34,8.66 84.58,8.76 83.63,8.97C82.71,9.16 81.96,9.5 81.4,10V18.86L78.04,19.14V5.78ZM87.77,3.76V0.28L91.13,0.04V3.62L87.77,3.76ZM87.75,19.14V5.78L91.11,5.49V18.86L87.75,19.14ZM98.66,19.26C97.57,19.26 96.65,18.99 95.9,18.45C95.15,17.89 94.77,16.9 94.77,15.5V8.03H92.9L93.04,5.49H94.87L95.2,2.56L98.13,2.15V5.49H100.91L100.77,8.03H98.15V15.47C98.15,15.95 98.27,16.3 98.51,16.5C98.75,16.71 99.11,16.82 99.57,16.82C99.83,16.82 100.26,16.77 100.87,16.67V19C100.8,19.02 100.55,19.06 100.1,19.14C99.67,19.22 99.19,19.26 98.66,19.26ZM106.99,19.31C105.39,19.31 104.17,18.68 103.32,17.42C102.47,16.14 102.05,14.44 102.05,12.33C102.05,10.18 102.48,8.48 103.35,7.22C104.21,5.95 105.47,5.32 107.11,5.32C108.68,5.32 109.88,5.95 110.71,7.22C111.55,8.48 111.96,10.18 111.96,12.33C111.96,12.46 111.94,12.55 111.89,12.62C111.84,12.66 111.82,12.7 111.82,12.71H105.41C105.41,13.75 105.55,14.7 105.82,15.57C106.09,16.42 106.51,16.84 107.07,16.84C107.69,16.84 108.15,16.48 108.46,15.76C108.76,15.04 108.92,14.66 108.94,14.63L111.48,15.59C111.15,16.74 110.61,17.66 109.87,18.33C109.14,18.98 108.18,19.31 106.99,19.31ZM108.65,10.77C108.65,9.81 108.52,9.05 108.27,8.49C108.01,7.93 107.61,7.65 107.07,7.65C106.79,7.65 106.53,7.78 106.27,8.06C106.02,8.33 105.81,8.7 105.65,9.18C105.49,9.66 105.41,10.19 105.41,10.77H108.65Z"
android:fillColor="#181B3A"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="111dp"
android:height="19dp"
android:viewportWidth="111"
android:viewportHeight="19">
<path
android:pathData="M9.08,18.14L3.82,6.89V17.86L0.68,18V0.14L4.35,-0L9.01,10.7V0.29L11.98,-0V17.86L9.08,18.14ZM19.43,18.31C17.83,18.31 16.61,17.68 15.76,16.42C14.91,15.14 14.49,13.44 14.49,11.33C14.49,9.18 14.92,7.48 15.78,6.22C16.65,4.95 17.9,4.32 19.55,4.32C21.12,4.32 22.32,4.95 23.15,6.22C23.98,7.48 24.4,9.18 24.4,11.33C24.4,11.46 24.38,11.55 24.33,11.62C24.28,11.66 24.26,11.7 24.26,11.71H17.85C17.85,12.75 17.98,13.7 18.26,14.57C18.53,15.42 18.94,15.84 19.5,15.84C20.13,15.84 20.59,15.48 20.9,14.76C21.2,14.04 21.36,13.66 21.38,13.63L23.92,14.59C23.58,15.74 23.05,16.66 22.31,17.33C21.58,17.98 20.62,18.31 19.43,18.31ZM21.09,9.77C21.09,8.81 20.96,8.05 20.7,7.49C20.45,6.93 20.05,6.65 19.5,6.65C19.23,6.65 18.97,6.78 18.71,7.06C18.46,7.33 18.25,7.7 18.09,8.18C17.93,8.66 17.85,9.19 17.85,9.77H21.09ZM28.16,18L25.4,4.75L28.83,4.49L29.98,12.14L31.69,4.78L34.5,4.49L35.96,12.05L37.42,4.78L40.16,4.63L37.06,18H34.35L32.82,9.24L30.92,18H28.16ZM47.69,4.78L51.05,4.49V6.67C51.53,5.98 52.12,5.43 52.83,5.02C53.55,4.58 54.46,4.37 55.56,4.37V7.66C54.99,7.66 54.23,7.76 53.28,7.97C52.35,8.16 51.61,8.5 51.05,9V17.86L47.69,18.14V4.78ZM61.69,18.31C60.09,18.31 58.87,17.68 58.02,16.42C57.17,15.14 56.75,13.44 56.75,11.33C56.75,9.18 57.18,7.48 58.04,6.22C58.91,4.95 60.16,4.32 61.81,4.32C63.38,4.32 64.58,4.95 65.41,6.22C66.24,7.48 66.66,9.18 66.66,11.33C66.66,11.46 66.63,11.55 66.59,11.62C66.54,11.66 66.51,11.7 66.51,11.71H60.11C60.11,12.75 60.24,13.7 60.51,14.57C60.79,15.42 61.2,15.84 61.76,15.84C62.39,15.84 62.85,15.48 63.15,14.76C63.46,14.04 63.62,13.66 63.63,13.63L66.18,14.59C65.84,15.74 65.31,16.66 64.57,17.33C63.83,17.98 62.87,18.31 61.69,18.31ZM63.35,9.77C63.35,8.81 63.22,8.05 62.96,7.49C62.71,6.93 62.31,6.65 61.76,6.65C61.49,6.65 61.23,6.78 60.97,7.06C60.71,7.33 60.51,7.7 60.35,8.18C60.19,8.66 60.11,9.19 60.11,9.77H63.35ZM72.87,18.31C71.84,18.31 70.96,17.99 70.23,17.35C69.51,16.71 68.96,15.86 68.59,14.81C68.23,13.75 68.04,12.61 68.04,11.38C68.04,10.16 68.22,9.02 68.57,7.94C68.94,6.87 69.49,6 70.23,5.33C70.96,4.66 71.87,4.32 72.94,4.32C73.75,4.32 74.48,4.53 75.12,4.94C75.78,5.34 76.29,5.91 76.66,6.65C77.03,7.38 77.23,8.22 77.26,9.17L74.07,9.31C74.07,9.25 74.03,8.99 73.97,8.54C73.92,8.1 73.8,7.73 73.61,7.44C73.43,7.15 73.2,7.01 72.91,7.01C72.27,7.01 71.87,7.45 71.69,8.33C71.51,9.19 71.43,10.17 71.43,11.26C71.43,12.33 71.51,13.3 71.69,14.18C71.88,15.05 72.32,15.48 73.01,15.48C73.87,15.48 74.32,14.63 74.35,12.94L77.19,13.25C76.98,14.91 76.56,16.18 75.94,17.04C75.31,17.89 74.29,18.31 72.87,18.31ZM83.77,18.31C82.15,18.31 80.91,17.67 80.03,16.39C79.16,15.11 78.73,13.41 78.73,11.28C78.73,9.17 79.16,7.48 80.03,6.22C80.89,4.95 82.14,4.32 83.77,4.32C85.39,4.32 86.63,4.95 87.51,6.22C88.41,7.48 88.86,9.17 88.86,11.28C88.86,13.39 88.41,15.1 87.51,16.39C86.63,17.67 85.39,18.31 83.77,18.31ZM83.77,15.82C84.49,15.82 84.95,15.38 85.16,14.5C85.37,13.62 85.47,12.54 85.47,11.26C85.47,9.98 85.37,8.9 85.16,8.02C84.95,7.14 84.49,6.7 83.77,6.7C83.05,6.7 82.59,7.11 82.4,7.94C82.21,8.78 82.11,9.79 82.11,10.99C82.11,12.46 82.21,13.64 82.4,14.52C82.59,15.38 83.05,15.82 83.77,15.82ZM91.14,4.78L94.5,4.49V6.67C94.98,5.98 95.58,5.43 96.28,5.02C97,4.58 97.91,4.37 99.02,4.37V7.66C98.44,7.66 97.68,7.76 96.74,7.97C95.81,8.16 95.06,8.5 94.5,9V17.86L91.14,18.14V4.78ZM104.14,18.31C101.54,18.31 100.25,15.96 100.25,11.26C100.25,8.81 100.57,7.04 101.21,5.95C101.85,4.86 102.85,4.32 104.21,4.32C104.78,4.32 105.33,4.51 105.86,4.9C106.39,5.26 106.78,5.73 107.01,6.29V0.43L110.4,-0V17.86L107.86,18.14L107.01,16.32C106.86,16.91 106.56,17.39 106.13,17.76C105.69,18.13 105.03,18.31 104.14,18.31ZM105.38,15.91C105.86,15.91 106.25,15.65 106.56,15.12C106.86,14.58 107.01,13.66 107.01,12.38V7.87C106.55,7.15 106.01,6.79 105.38,6.79C104.86,6.79 104.46,6.98 104.21,7.37C103.97,7.74 103.81,8.22 103.73,8.83C103.66,9.44 103.63,10.25 103.63,11.26C103.63,12.28 103.66,13.11 103.73,13.75C103.81,14.39 103.97,14.91 104.23,15.31C104.49,15.71 104.87,15.91 105.38,15.91Z"
android:fillColor="#181B3A"/>
</vector>

View File

@ -1,5 +1,29 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#4156C8" /> <!-- 设置圆形的填充颜色 -->
<size android:width="60dp" android:height="60dp" /> <!-- 设置圆形的直径 -->
<corners android:radius="30dp" /> <!-- 圆角半径设置为宽度或高度的一半,以形成圆形 -->
</shape>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 外层圆环 -->
<item>
<shape android:shape="oval">
<solid android:color="#80AABCFF" /> <!-- 圆环的颜色 -->
<!-- 外层圆环的直径中心圆100dp + 空隙5dp*2 + 圆环10dp*2 = 130dp -->
<size android:width="108dp" android:height="108dp" />
</shape>
</item>
<!-- 白色空隙层 -->
<item android:top="5dp" android:bottom="5dp" android:left="5dp" android:right="5dp">
<shape android:shape="oval">
<solid android:color="#FFFFFF" /> <!-- 空隙的颜色 -->
<!-- 白色空隙层的直径中心圆100dp + 空隙5dp*2 = 110dp -->
<size android:width="102dp" android:height="102dp" />
</shape>
</item>
<!-- 中心圆 -->
<item android:top="7dp" android:bottom="7dp" android:left="7dp" android:right="7dp">
<shape android:shape="oval">
<solid android:color="#AAB2FF" /> <!-- 中心圆的颜色 -->
<!-- 中心圆的直径100dp -->
<size android:width="100dp" android:height="100dp" />
</shape>
</item>
</layer-list>

View File

@ -2,7 +2,7 @@
<!-- 背景部分 -->
<item android:left="2dp" android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#ABABAB" /> <!-- 背景颜色 -->
<solid android:color="#EDEFFF" /> <!-- 背景颜色 -->
<corners android:radius="16dp" /> <!-- 圆角半径 -->
</shape>
</item>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 外层圆环 -->
<item>
<shape android:shape="oval">
<solid android:color="#CC505BC8" /> <!-- 圆环的颜色 -->
<!-- 外层圆环的直径中心圆100dp + 空隙5dp*2 + 圆环10dp*2 = 130dp -->
<size android:width="110dp" android:height="110dp" />
</shape>
</item>
<!-- 中心圆 -->
<item android:top="5dp" android:bottom="5dp" android:left="5dp" android:right="5dp">
<shape android:shape="oval">
<solid android:color="#505BC8" /> <!-- 中心圆的颜色 -->
<!-- 中心圆的直径100dp -->
<size android:width="100dp" android:height="100dp" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,33 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 阴影部分 -->
<item>
<shape android:shape="rectangle">
<!-- 阴影颜色,增加不透明度 -->
<solid android:color="#181B3A" />
<corners android:radius="16dp" />
<padding
android:left="1dp"
android:bottom="1dp"
android:right="1dp"
android:top="1dp" />
</shape>
</item>
<!-- 背景部分 -->
<item android:left="0dp" android:bottom="0dp">
<shape android:shape="rectangle">
<solid android:color="#FFFFFF" /> <!-- 背景颜色 -->
<corners android:radius="16dp" /> <!-- 圆角半径 -->
</shape>
</item>
<!-- 边框部分 -->
<item>
<shape android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#000000" /> <!-- 边框颜色和宽度 -->
<corners android:radius="16dp" /> <!-- 圆角半径 -->
</shape>
</item>
</layer-list>

View File

@ -8,8 +8,8 @@
<padding
android:left="4dp"
android:bottom="4dp"
android:right="0dp"
android:top="0dp" />
android:right="1dp"
android:top="1dp" />
</shape>
</item>

View File

@ -5,7 +5,7 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment.AirHornActivity">
tools:context=".Activity.AirHornActivity">
<ImageView
@ -18,14 +18,17 @@
android:layout_marginBottom="20dp"
android:src="@drawable/black_back" />
<ImageView
<TextView
android:id="@+id/air_horn_title"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_marginTop="18dp"
android:layout_marginBottom="20dp"
android:src="@drawable/sound_title" />
android:text="Airhorn"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/air_horn_recyclerview"

View File

@ -5,7 +5,7 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Main.MainActivity">
tools:context=".Activity.MainActivity">
<androidx.viewpager2.widget.ViewPager2

View File

@ -1,22 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondFragment.NewRecordActivity">
tools:context=".Activity.NewRecordActivity">
<ImageView
android:id="@+id/new_record_back"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentStart="true"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/setting" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/black_back" />
<ImageView
android:id="@+id/new_record_title"
@ -25,15 +26,17 @@
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/sound_title" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/new_record" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_below="@+id/new_record_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
app:layout_constraintTop_toTopOf="@+id/new_record_title"
app:layout_constraintBottom_toTopOf="@+id/new_record_time"
app:lottie_fileName="Animation.json"
app:lottie_loop="true" />
@ -42,7 +45,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/animation_view"
android:layout_centerHorizontal="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="50dp"
android:textSize="44sp"
android:textColor="@color/blue"
@ -50,13 +56,14 @@
<ImageButton
android:id="@+id/new_record_record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/new_record_time"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:layout_width="110dp"
android:layout_height="110dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="100dp"
android:background="@drawable/round"
android:src="@drawable/microphone" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -69,6 +69,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/white"
android:text="Airhorn"
android:textSize="17sp"
app:layout_constraintBottom_toBottomOf="@id/rounded_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -113,11 +116,11 @@
<ImageView
android:id="@+id/playback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginTop="50dp"
android:layout_marginBottom="100dp"
android:background="@drawable/round"
android:background="@drawable/round_play"
android:src="@drawable/playback"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Activity.PrivacyActivity">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="15dp"
android:src="@drawable/black_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
@ -7,82 +7,87 @@
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
tools:context=".SecondFragment.SaveRecordActivity">
tools:context=".Activity.SaveRecordActivity">
<ImageView
android:id="@+id/save_record_back"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentStart="true"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/black_back" />
android:src="@drawable/black_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/save_record_title"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/sound_title" />
android:src="@drawable/new_record"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/save_record_image"
android:layout_width="200dp"
android:layout_height="150dp"
android:layout_below="@+id/save_record_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:src="@mipmap/savesound" />
android:src="@mipmap/savesound"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/save_record_title" />
<TextView
android:id="@+id/save_record_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/save_record_image"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:textSize="44sp"
android:layout_marginTop="16dp"
android:text="00:00:00"
android:textColor="@color/blue"
android:text="00:00:00" />
android:textSize="19sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/save_record_image" />
<EditText
android:id="@+id/save_record_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/save_record_time"
android:layout_centerHorizontal="true"
android:hint="@string/give_name"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginStart="46dp"
android:layout_marginEnd="46dp"
android:background="@drawable/round_edit_text"
android:layout_marginTop="50dp" />
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/save_record_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/save_record_name"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:background="@drawable/round"
android:src="@drawable/playback" />
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginBottom="80dp"
android:background="@drawable/round_play"
android:src="@drawable/playback"
app:layout_constraintBottom_toTopOf="@+id/save_record_save"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/save_record_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginEnd="24dp"
android:layout_marginBottom="46dp"
android:backgroundTint="@color/blue"
android:text="Save Sound"/>
android:text="Save Sound"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -41,8 +41,8 @@
<ImageView
android:id="@+id/cancel_button"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_width="44dp"
android:layout_height="44dp"
android:src="@drawable/cancel"
android:layout_below="@id/all_option"
android:layout_marginTop="16dp"

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ThirdFragment.FavoriteFragment">
tools:context=".Fragment.FavoriteFragment">
<ImageView
@ -13,7 +13,7 @@
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/sound_title" />
android:src="@drawable/my_favorite" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/favorite_recyclerview"

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondFragment.RadioFragment">
tools:context=".Fragment.RadioFragment">
<ImageView
android:id="@+id/fragment_sound_title"
@ -12,7 +12,7 @@
android:layout_height="32dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/sound_title"
android:src="@drawable/create_sounds"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment.SoundFragment">
tools:context=".Fragment.SoundFragment">
<ImageView

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rounded_border">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/item_imageview_airhorn"
android:layout_width="match_parent"
android:layout_height="127dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/item_textview_airhorn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textStyle="bold"
android:text="Airhorn"
android:textSize="10sp"
android:shadowColor="#80FFFFFF"
android:shadowDx="0.5"
android:shadowDy="0.5"
android:shadowRadius="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,14 +2,25 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:background="@drawable/rounded_border_shadow">
<ImageView
android:id="@+id/item_imageview_favorite"
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="110dp"
android:background="@drawable/rounded_border_shadow"
app:layout_constraintTop_toTopOf="parent" />
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/item_imageview_favorite"
android:layout_width="match_parent"
android:layout_height="110dp" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/item_imageview_text"
@ -17,10 +28,8 @@
android:layout_height="wrap_content"
android:layout_marginBottom="22dp"
android:background="@color/tint"
android:text="@string/app_name"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/item_imageview_favorite"
app:layout_constraintEnd_toEndOf="@+id/item_imageview_favorite"
app:layout_constraintStart_toStartOf="@id/item_imageview_favorite" />
android:text="@string/app_name"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,24 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_radio_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="16dp"
android:padding="12dp"
android:background="@drawable/round_create">
>
<!-- 滑动后显示的 ImageView -->
<!-- <ImageView-->
<!-- android:id="@+id/item_delete"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:src="@drawable/delete"-->
<!-- android:layout_gravity="end|center_vertical" />-->
<!-- 可滑动的内容区 -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/item_con"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
android:background="@drawable/round_create"
android:padding="12dp"
android:layout_gravity="start">
<!-- 图片 -->
<ImageView
@ -26,9 +32,9 @@
android:layout_width="80dp"
android:layout_height="60dp"
android:src="@mipmap/savesound"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
app:layout_constraintTop_toTopOf="parent" />
<!-- 标题 -->
<TextView
@ -37,21 +43,34 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/snoring"
app:layout_constraintEnd_toStartOf="@+id/duration"
app:layout_constraintStart_toEndOf="@+id/item_imageview_radio"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@+id/duration" />
app:layout_constraintTop_toTopOf="parent" />
<!-- 日期 -->
<TextView
android:id="@+id/date"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="date"
android:text="23 May 2024"
android:textColor="#ABABAB"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@+id/item_imageview_radio"
app:layout_constraintTop_toBottomOf="@+id/title"
app:layout_constraintEnd_toStartOf="@+id/duration" />
app:layout_constraintTop_toBottomOf="@+id/title" />
<!-- 竖线 -->
<View
android:id="@+id/divider"
android:layout_width="1dp"
android:layout_height="12dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="#ABABAB"
app:layout_constraintBottom_toBottomOf="@+id/date"
app:layout_constraintStart_toEndOf="@+id/date"
app:layout_constraintTop_toTopOf="@+id/date" />
<!-- 时间 -->
<TextView
@ -59,10 +78,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="time"
app:layout_constraintStart_toEndOf="@+id/date"
app:layout_constraintTop_toBottomOf="@+id/title"
app:layout_constraintBottom_toBottomOf="@+id/date" />
android:layout_marginTop="8dp"
android:text="07:00pm"
android:textColor="#ABABAB"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@+id/divider"
app:layout_constraintTop_toBottomOf="@+id/title" />
<!-- 时长 -->
<TextView
@ -70,6 +91,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="File duration"
android:textColor="#ABABAB"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@ -85,39 +107,4 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- 左滑显示的 LinearLayout -->
<LinearLayout
android:id="@+id/item_linear"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_gravity="end"
app:layout_constraintStart_toEndOf="@+id/item_con"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<!-- 编辑按钮 -->
<TextView
android:id="@+id/jianyi_zhiding"
android:layout_width="64dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="编辑"
android:textAlignment="center"
android:textColor="#222222"
android:background="#eaeaea" />
<!-- 删除按钮 -->
<TextView
android:id="@+id/jianyi_delete"
android:layout_width="64dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="删除"
android:textAlignment="center"
android:textColor="#FFFFFF"
android:background="#D52619" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -10,10 +10,10 @@
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/item_imageview_sound"
@ -24,10 +24,17 @@
<TextView
android:id="@+id/item_textview_sound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="20dp"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_marginStart="12dp"
android:layout_marginTop="6dp"
app:layout_constraintTop_toTopOf="parent"/>
android:textColor="@color/black"
android:textStyle="bold"
android:shadowColor="#80FFFFFF"
android:shadowDx="0.5"
android:shadowDy="0.5"
android:shadowRadius="1"
android:text="Airhorn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -5,23 +5,24 @@
android:layout_centerHorizontal="true">
<!-- 第一个 ImageView 在最上面居中 -->
<ImageView
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="12dp"
android:src="@drawable/sound_title" />
android:text="Settings"
android:textSize="19sp" />
<ImageView
android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginTop="12dp"
android:layout_marginRight="5dp"
android:src="@drawable/setting" />
android:layout_marginRight="16dp"
android:src="@drawable/cancel" />
<LinearLayout
android:id="@+id/privacy"
@ -92,20 +93,15 @@
android:text="Version Number" />
<TextView
android:id="@+id/version_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1.0.0"/>
android:text="1.0.0" />
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
<!-- 底部的空白距离 -->
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/version"
android:layout_marginTop="10dp"
android:max="100"
android:progress="0" />
android:layout_height="30dp"
android:layout_below="@+id/version" />
</RelativeLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -4,9 +4,12 @@
<color name="white">#FFFFFFFF</color>
<color name="whitea">#9F2929</color>
<color name="blue">#505BC8</color>
<color name="tint">#ABABAB</color>
<color name="color1">#9F2929</color>
<color name="color2">#5164CC</color>
<color name="color3">#405F1D</color>
<color name="tint">#CCFFFFFF</color>
<color name="color2">#EDA7FF</color>
<color name="color3">#F6BB54</color>
<color name="color4">#A0FFFC</color>
<color name="color5">#FF8ACE</color>
<color name="color6">#7FD788</color>
<color name="color7">#9D9BFF</color>
</resources>

View File

@ -1,7 +1,8 @@
<resources>
<string name="app_name">PrankPartyDemo</string>
<string name="app_name">PrankParty</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="give_name">Give your created sound a name</string>
<string name="snoring">Snoring</string>
<string name="gp">https://play.google.com/store/apps/details?id=%s</string>
</resources>

View File

@ -19,6 +19,6 @@ dependencyResolutionManagement {
}
}
rootProject.name = "PrankPartyDemo"
rootProject.name = "PrankParty"
include(":app")