无知的我接触到了Android。
特点是 将会解决后端数据问题、解决数据库版本等等问题。
对于完整的笔记,我已经在本地完成,记录了从开始学习到制作完成的详细过程。但是如果没有人需要,后续不会再更新了。
1 工具
1.1 使用Adobe xd设计原型图
1.2 使用PxCook生成设计安卓代码
1.3 PxCook需要基于安装Adobe air
2 创建项目
3 选择模拟器型号
选择苹果尺寸和苹果分辨率
<resources>
<color name="colorPrimary">#6200EEcolor>
<color name="colorPrimaryDark">#3700B3color>
<color name="colorAccent">#03DAC5color>
<color name="white">#ffffffcolor>
<color name="black">#000000color>
<color name="dividser">#e8e7e7color>
<color name="skin_text_white">#ffffffcolor>
<color name="skin_topbar_bg_color">#344261color>
resources>
位置:values文件夹下,创建dimens.xml文件
所有尺寸的配置都在此配置
<resources>
<dimen name="dimen_90dp">90dpdimen>
<dimen name="dimen_44dp">44dpdimen>
<dimen name="size_20sp">20spdimen>
resources>
位置:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/splash"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/dimen_90dp"
android:paddingLeft="@dimen/dimen_44dp"
android:paddingRight="@dimen/dimen_44dp">
<Button
android:id="@+id/btn_login"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:text="@string/login"
android:textColor="#ffffff"
android:background="@drawable/shape_login_btn"
android:textSize="@dimen/size_20sp" />
<Button
android:id="@+id/btn_register"
android:layout_width="100dp"
android:layout_height="40dp"
android:background="@drawable/shape_register_btn"
android:layout_alignParentRight="true"
android:text="@string/register"
android:textColor="#ffffff"
android:textSize="20sp" />
</RelativeLayout>
</RelativeLayout>
还需:1.编写values相关配置 2.引入相关资源文件,其中背景图存放到mipmap-xxxhdpi文件下
位置:drawble文件夹,创建xml资源文件:1.根元素为shape的;2.名字为shape_login_btn
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="9dp" />
<solid android:color="#c75fe768" />
shape>
位置:AndroidManifest.xml,修改
android:theme="@style/Theme.AppCompat.NoActionBar">
创建activity文件夹,其中空活动:LoginActivity
位置:activity_login.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".activity.LoginActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="329dp"
android:scaleType="fitXY"
android:src="@mipmap/login" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="278dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="230dp"
android:layout_marginLeft="18dp"
android:layout_marginRight="18dp"
android:background="@drawable/shape_login_form"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="43dp"
android:paddingRight="31dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@mipmap/account" />
<EditText
android:id="@+id/et_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:background="@null"
android:hint="@string/account_hint"
android:textColor="#000000"
android:text="root"
android:textColorHint="#bcbcbc"
android:textSize="18sp" />
LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="23dp"
android:layout_marginBottom="23dp"
android:background="#e8e7e7" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@mipmap/pwd" />
<EditText
android:id="@+id/et_pwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:background="@null"
android:text="123456"
android:inputType="textPassword"
android:hint="@string/pwd_hint"
android:textColor="#000000"
android:textColorHint="#bcbcbc"
android:textSize="18sp" />
LinearLayout>
LinearLayout>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginLeft="18dp"
android:layout_marginTop="67dp"
android:layout_marginRight="18dp"
android:background="@drawable/shape_big_login_btn"
android:text="@string/login"
android:textColor="#ffffff"
android:textSize="24sp" />
LinearLayout>
RelativeLayout>
在shape_login_form.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<padding
android:bottom="2dp"
android:left="1.5dp"
android:right="2dp"
android:top="1.5dp" />
<solid android:color="#F2F2F2" />
<corners android:radius="8dp" />
shape>
item>
<item>
<shape
android:shape="rectangle"
android:useLevel="false">
<solid android:color="#ffffff" />
<corners android:radius="10dp" />
<padding
android:bottom="10dp"
android:left="10dp"
android:right="10dp"
android:top="10dp" />
shape>
item>
layer-list>
// 原理:两个卡片叠加实现
在activity文件夹下创建空activity,名字:RegisterActivity
//便捷:复制登录页面,修改即可
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".activity.LoginActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="329dp"
android:scaleType="fitXY"
android:src="@mipmap/login" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="278dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="230dp"
android:layout_marginLeft="18dp"
android:layout_marginRight="18dp"
android:background="@drawable/shape_login_form"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="43dp"
android:paddingRight="31dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@mipmap/account" />
<EditText
android:id="@+id/et_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:background="@null"
android:hint="@string/account_hint"
android:textColor="#000000"
android:textColorHint="#bcbcbc"
android:textSize="18sp" />
LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="23dp"
android:layout_marginBottom="23dp"
android:background="@color/divider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@mipmap/pwd" />
<EditText
android:id="@+id/et_pwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:background="@null"
android:inputType="textPassword"
android:hint="@string/pwd_hint"
android:textColor="@color/black"
android:textColorHint="#bcbcbc"
android:textSize="18sp" />
LinearLayout>
LinearLayout>
<Button
android:id="@id/btn_register"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginLeft="18dp"
android:layout_marginTop="67dp"
android:layout_marginRight="18dp"
android:background="@drawable/shape_big_register_btn"
android:text="@string/register"
android:textColor="@color/white"
android:textSize="24sp" />
LinearLayout>
RelativeLayout>
package com.ttit.myapp;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.ttit.myapp.activity.BaseActivity;
import com.ttit.myapp.activity.LoginActivity;
import com.ttit.myapp.activity.RegisterActivity;
public class MainActivity extends BaseActivity {
private Button btnLogin;
private Button btnRegister;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//登录按钮
btnLogin = findViewById(R.id.btn_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Intent intent = new Intent(MainActivity.this, LoginActivity.class);
// startActivity(intent);
navigateTo(LoginActivity.class);
}
});
//注册按钮
btnRegister = findViewById(R.id.btn_register);
btnRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Intent intent = new Intent(MainActivity.this, RegisterActivity.class);
// startActivity(intent);
navigateTo(RegisterActivity.class);
}
});
}
}
//活动跳转
public void navigateTo(Class cls) {
Intent in = new Intent(mContext, cls);
startActivity(in);
}
易错改正:记得继承类
package com.ttit.myapp.activity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.ttit.myapp.R;
import com.ttit.myapp.util.StringUtils;
public class LoginActivity extends BaseActivity {
private Button btnLogin;
private EditText etAccount;
private EditText etPwd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//登录验证
etAccount = findViewById(R.id.et_account);
etPwd = findViewById(R.id.et_pwd);
btnLogin = findViewById(R.id.btn_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String account = etAccount.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();
login(account, pwd);
}
});
}
private void login(String account, String pwd) {
if (StringUtils.isEmpty(account)) {
showToast("请输入账号");
return;
}
if (StringUtils.isEmpty(pwd)) {
showToast("请输入密码");
return;
}
}
}
1.编写活动跳转方法
2.编写提示信息方法
package com.ttit.myapp.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public abstract class BaseActivity extends AppCompatActivity {
public Context mContext;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
}
//提示信息
public void showToast(String msg) {
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
//活动跳转
public void navigateTo(Class cls) {
Intent in = new Intent(mContext, cls);
startActivity(in);
}
}
创建工具类
package com.ttit.myapp.util;
public class StringUtils {
public static boolean isEmpty(String str) {
if (str == null || str.length() <= 0) {
return true;
} else {
return false;
}
}
}
是否跳转成功以及验证是否成功
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.10.0'
//网址:https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp/3.10.0
简写版:
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
//点击synoc now 效果:开始下载依赖
1 请求代码
private void login(String account, String pwd) {
if (StringUtils.isEmpty(account)) {
showToast("请输入账号");
return;
}
if (StringUtils.isEmpty(pwd)) {
showToast("请输入密码");
return;
}
// 第一步创建OKHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.build();
Map m = new HashMap();
m.put("mobile", account);
m.put("password", pwd);
JSONObject jsonObject = new JSONObject(m);
String jsonStr = jsonObject.toString();
RequestBody requestBodyJson =
RequestBody.create(MediaType.parse("application/json;charset=utf-8")
, jsonStr);
//第三步创建Rquest
Request request = new Request.Builder()
.url(AppConfig.BASE_URl + "/app/login")
.addHeader("contentType", "application/json;charset=UTF-8")
.post(requestBodyJson)
.build();
//第四步创建call回调对象
final Call call = client.newCall(request);
//第五步发起请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("onFailure", e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String result = response.body().string();
//ui必须在主线程处理 //原因:若在子线程处理会报运行时错误
runOnUiThread(new Runnable() {
@Override
public void run() {
showToast(result);
}
});
}
});
}
2 在其中,需要封装请求的基础地址:
package com.ttit.myapp.util;
/**
*@author wuzhideren
*/
public class AppConfig {
public static final String BASE_URl = "http://192.168.31.32:8080/renren-fast";
}
3 需要取消只能发送https的限制:
1 创建.xml文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4MCuz2Y0-1643208228100)(视频资讯app.assets/image-20220117191714496.png)]
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
network-security-config>
2 在核心配置文件中引入该.xml文件:
// 原因:在27版本以上默认只能发送https请求
package com.ttit.myapp.activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.ttit.myapp.R;
import com.ttit.myapp.api.Api;
import com.ttit.myapp.api.ApiConfig;
import com.ttit.myapp.api.TtitCallback;
import com.ttit.myapp.util.StringUtils;
import java.util.HashMap;
public class RegisterActivity extends BaseActivity {
private Button btnRegister;
private EditText etAccount;
private EditText etPwd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
//登录验证
etAccount = findViewById(R.id.et_account);
etPwd = findViewById(R.id.et_pwd);
btnRegister = findViewById(R.id.btn_register);
btnRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String account = etAccount.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();
register(account, pwd);
}
});
}
private void register(String account, String pwd) {
if (StringUtils.isEmpty(account)) {
showToast("请输入账号");
return;
}
if (StringUtils.isEmpty(pwd)) {
showToast("请输入密码");
return;
}
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("mobile", account);
params.put("password", pwd);
Api.config(ApiConfig.REGISTER, params).postRequest(this,new TtitCallback() {
@Override
public void onSuccess(final String res) {
runOnUiThread(new Runnable() {
@Override
public void run() {
showToast(res);
}
});
}
@Override
public void onFailure(Exception e) {
Log.e("onFailure", e.toString());
}
});
}
}
//处理子线程ui
public void showToastSync(String msg) {
//令子线程可以有ui显示,而不报错
Looper.prepare();
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
Looper.loop();
}
//作用:处理子线程ui
package com.ttit.myapp.entity;
public class LoginResponse {
/**
* msg : success
* code : 0
* expire : 604800
* token : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2IiwiaWF0IjoxNTkyNDg2OTQzLCJleHAiOjE1OTMwOTE3NDN9.f5sxyG60GyDlj0FcZEmPAADiLHX_pATrvicxbADqvRqYurYQC5s0KAjw5XgHS4gpk-qUSwWtcJpY_nJjYf_2Dw
*/
private String msg;
private int code;
private int expire;
private String token;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public int getExpire() {
return expire;
}
public void setExpire(int expire) {
this.expire = expire;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
//扩展-快速生成方法:使用GsonFormat插件,把json格式字符串快速转化实体类。
implementation 'com.google.code.gson:gson:2.8.5'
package com.ttit.myapp.api;
public interface TtitCallback {
void onSuccess(String res);
void onFailure(Exception e);
}
package com.ttit.myapp.api;
public class ApiConfig {
public static final String BASE_URl = "http://192.168.31.32:8080/renren-fast";
public static final String LOGIN = "/app/login"; //登录
public static final String REGISTER = "/app/register";//注册
}
package com.ttit.myapp.api;
import static android.content.Context.MODE_PRIVATE;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import com.ttit.myapp.activity.LoginActivity;
import com.ttit.myapp.util.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class Api {
private static OkHttpClient client;
private static String requestUrl;
private static HashMap<String, Object> mParams;
public static Api api = new Api();
public Api() {
}
public static Api config(String url, HashMap<String, Object> params) {
client = new OkHttpClient.Builder()
.build();
requestUrl = ApiConfig.BASE_URl + url;
mParams = params;
return api;
}
public void postRequest(Context context, final TtitCallback callback) {
SharedPreferences sp = context.getSharedPreferences("sp_ttit", MODE_PRIVATE);
String token = sp.getString("token", "");
JSONObject jsonObject = new JSONObject(mParams);
String jsonStr = jsonObject.toString();
RequestBody requestBodyJson =
RequestBody.create(MediaType.parse("application/json;charset=utf-8")
, jsonStr);
//第三步创建Rquest
Request request = new Request.Builder()
.url(requestUrl)
.addHeader("contentType", "application/json;charset=UTF-8")
.addHeader("token", token)
.post(requestBodyJson)
.build();
//第四步创建call回调对象
final Call call = client.newCall(request);
//第五步发起请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("onFailure", e.getMessage());
callback.onFailure(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String result = response.body().string();
callback.onSuccess(result);
}
});
}
}
位置:在activity文件夹下,LoginActivity.class
private void login(String account, String pwd) {
if (StringUtils.isEmpty(account)) {
showToast("请输入账号");
return;
}
if (StringUtils.isEmpty(pwd)) {
showToast("请输入密码");
return;
}
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("mobile", account);
params.put("password", pwd);
Api.config(ApiConfig.LOGIN, params).postRequest(this,new TtitCallback() {
@Override
public void onSuccess(final String res) {
Log.e("onSuccess", res);
//json格式字符串转化为类对象
Gson gson = new Gson();
LoginResponse loginResponse = gson.fromJson(res, LoginResponse.class);
if (loginResponse.getCode() == 0) {
String token = loginResponse.getToken();
insertVal("token", token);
showToastSync("登录成功");
} else {
showToastSync("登录失败");
}
}
@Override
public void onFailure(Exception e) {
}
});
}
//处理子线程ui
public void showToastSync(String msg) {
//令子线程可以有ui显示,而不报错
Looper.prepare();
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
Looper.loop();//易错修正:其后代码不会立即执行
}
跳转到主页:
navigateTo(HomeActivity.class);//若写在83行,则因为线程原因不会执行该代码。
implementation 'com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar'
// 目的:为了使用CommonTabLayout控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tl="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical"
tools:context=".activity.HomeActivity">
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/divider" />
<com.flyco.tablayout.CommonTabLayout
android:id="@+id/commonTabLayout"
android:layout_width="match_parent"
android:layout_height="66dp"
android:background="#ffffff"
tl:tl_iconHeight="30dp"
tl:tl_iconWidth="30dp"
tl:tl_indicator_color="#2C97DE"
tl:tl_indicator_height="0dp"
tl:tl_textSelectColor="#0025ff"
tl:tl_textUnselectColor="#454544"
tl:tl_textsize="14sp"
tl:tl_underline_color="#DDDDDD"
tl:tl_underline_height="1dp" />
LinearLayout>
易错改正:不能使用viewpager2
package com.ttit.myapp.activity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import android.os.Bundle;
import com.flyco.tablayout.CommonTabLayout;
import com.flyco.tablayout.listener.CustomTabEntity;
import com.ttit.myapp.R;
import java.util.ArrayList;
public class HomeActivity extends AppCompatActivity {
//导航栏字符串
private String[] mTitles = {"首页", "资讯", "我的"};
//导航栏图片
private int[] mIconUnselectIds = {
R.mipmap.home_unselect, R.mipmap.collect_unselect,
R.mipmap.my_unselect};
private int[] mIconSelectIds = {
R.mipmap.home_selected, R.mipmap.collect_selected,
R.mipmap.my_selected};
//装入导航栏分页的页面,就是fragment
private ArrayList<Fragment> mFragments = new ArrayList<>();
private ArrayList<CustomTabEntity> mTabEntities = new ArrayList<>();
//这是分页
private ViewPager viewPager;
private CommonTabLayout commonTabLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
}
}
新建三个基本fragment,并且改传参实例化方法为无参的
其中之一:
package com.ttit.myapp.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import com.ttit.myapp.R;
/**
* A simple {@link Fragment} subclass.
* Use the {@link CollectFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class CollectFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
public CollectFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment CollectFragment.
*/
// TODO: Rename and change types and number of parameters
public static CollectFragment newInstance() {
CollectFragment fragment = new CollectFragment();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_collect, container, false);
}
}
新建、继承、配置
package com.ttit.myapp.adapter;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.ArrayList;
//用于设置pager的页面
public class MyPagerAdapter extends FragmentPagerAdapter {
private String[] mTitles;
private ArrayList<Fragment> mFragments;
//通过构造器传入页面属性,以设置
public MyPagerAdapter(FragmentManager fm, String[] titles, ArrayList<Fragment> fragments) {
super(fm);
this.mTitles = titles;
this.mFragments = fragments;
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
}
package com.ttit.myapp.entity;
import com.flyco.tablayout.listener.CustomTabEntity;
public class TabEntity implements CustomTabEntity {
public String title;
public int selectedIcon;
public int unSelectedIcon;
public TabEntity(String title, int selectedIcon, int unSelectedIcon) {
this.title = title;
this.selectedIcon = selectedIcon;
this.unSelectedIcon = unSelectedIcon;
}
@Override
public String getTabTitle() {
return title;
}
@Override
public int getTabSelectedIcon() {
return selectedIcon;
}
@Override
public int getTabUnselectedIcon() {
return unSelectedIcon;
}
}
package com.ttit.myapp.activity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import android.os.Bundle;
import com.flyco.tablayout.CommonTabLayout;
import com.flyco.tablayout.listener.CustomTabEntity;
import com.flyco.tablayout.listener.OnTabSelectListener;
import com.ttit.myapp.R;
import com.ttit.myapp.adapter.MyPagerAdapter;
import com.ttit.myapp.entity.TabEntity;
import com.ttit.myapp.fragment.CollectFragment;
import com.ttit.myapp.fragment.HomeFragment;
import com.ttit.myapp.fragment.MyFragment;
import java.util.ArrayList;
public class HomeActivity extends AppCompatActivity {
//导航栏字符串
private String[] mTitles = {"首页", "资讯", "我的"};
//导航栏图片
private int[] mIconUnselectIds = {
R.mipmap.home_unselect, R.mipmap.collect_unselect,
R.mipmap.my_unselect};
private int[] mIconSelectIds = {
R.mipmap.home_selected, R.mipmap.collect_selected,
R.mipmap.my_selected};
//装入导航栏分页的页面,就是fragment
private ArrayList<Fragment> mFragments = new ArrayList<>();
private ArrayList<CustomTabEntity> mTabEntities = new ArrayList<>();
//这是分页
private ViewPager viewPager;
//这是四个按钮
private CommonTabLayout commonTabLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
viewPager = findViewById(R.id.viewpager);
commonTabLayout = findViewById(R.id.commonTabLayout);
//添加fra实例化对象入集合
mFragments.add(HomeFragment.newInstance());
mFragments.add(CollectFragment.newInstance());
mFragments.add(MyFragment.newInstance());
//添加tab实例化对象入集合
for (int i = 0; i < mTitles.length; i++) {
mTabEntities.add(new TabEntity(mTitles[i], mIconSelectIds[i], mIconUnselectIds[i]));
}
//实现点击tab,页面切换
commonTabLayout.setOnTabSelectListener(new OnTabSelectListener() {
//@position frag中集合的下标
@Override
public void onTabSelect(int position) {
//设置分页的当前页
viewPager.setCurrentItem(position);
}
@Override
public void onTabReselect(int position) {
}
});
//为tab视图对象,设置tab集合
commonTabLayout.setTabData(mTabEntities);
//为pager添加上述内容
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), mTitles, mFragments));
}
}
//导航栏对应切换
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
//当页面切换时,传入当前页面坐标,让导航栏对应地切换
@Override
public void onPageSelected(int position) {
commonTabLayout.setCurrentTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
1 新建自定义FixedViewPager
2 继承ViewPager
3 修改public void setCurrentItem(int item){}
内调用的父方法参数smoothScroll
:false
package com.ttit.myapp.view;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;
public class FixedViewPager extends ViewPager {
public FixedViewPager(@NonNull Context context) {
super(context);
}
public FixedViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setCurrentItem(int item) {
super.setCurrentItem(item, false);
}
}
修改布局文件的控件为自定义
<com.ttit.myapp.view.FixedViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
//基本代码封装
protected abstract int initLayout();
protected abstract void initView();
protected abstract void initData();
package com.ttit.myapp.activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Looper;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public abstract class BaseActivity extends AppCompatActivity {
public Context mContext;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
//调用封装方法,传入各实现类的参数
setContentView(initLayout());
initView();
initData();
}
//基本代码封装
protected abstract int initLayout();
protected abstract void initView();
protected abstract void initData();
//提示信息
public void showToast(String msg) {
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
//处理子线程ui
public void showToastSync(String msg) {
//令子线程可以有ui显示,而不报错
Looper.prepare();
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
Looper.loop();//易错修正:其后代码不会立即执行
}
//活动跳转
public void navigateTo(Class cls) {
Intent in = new Intent(mContext, cls);
startActivity(in);
}
//sp存储
protected void insertVal(String key, String val) {
SharedPreferences sp = getSharedPreferences("sp_ttit", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString(key, val);
editor.commit();
}
}
package com.ttit.myapp.activity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import android.os.Bundle;
import com.flyco.tablayout.CommonTabLayout;
import com.flyco.tablayout.listener.CustomTabEntity;
import com.flyco.tablayout.listener.OnTabSelectListener;
import com.ttit.myapp.R;
import com.ttit.myapp.adapter.MyPagerAdapter;
import com.ttit.myapp.entity.TabEntity;
import com.ttit.myapp.fragment.CollectFragment;
import com.ttit.myapp.fragment.HomeFragment;
import com.ttit.myapp.fragment.MyFragment;
import java.util.ArrayList;
public class HomeActivity extends BaseActivity {
//导航栏字符串
private String[] mTitles = {"首页", "资讯", "我的"};
//导航栏图片
private int[] mIconUnselectIds = {
R.mipmap.home_unselect, R.mipmap.collect_unselect,
R.mipmap.my_unselect};
private int[] mIconSelectIds = {
R.mipmap.home_selected, R.mipmap.collect_selected,
R.mipmap.my_selected};
//装入导航栏分页的页面,就是fragment
private ArrayList<Fragment> mFragments = new ArrayList<>();
private ArrayList<CustomTabEntity> mTabEntities = new ArrayList<>();
//这是分页
private ViewPager viewPager;
//这是四个按钮
private CommonTabLayout commonTabLayout;
@Override
protected int initLayout() {
return R.layout.activity_home;
}
@Override
protected void initView() {
viewPager = findViewById(R.id.viewpager);
commonTabLayout = findViewById(R.id.commonTabLayout);
}
@Override
protected void initData() {
//添加fra实例化对象入集合
mFragments.add(HomeFragment.newInstance());
mFragments.add(CollectFragment.newInstance());
mFragments.add(MyFragment.newInstance());
//添加tab实例化对象入集合
for (int i = 0; i < mTitles.length; i++) {
mTabEntities.add(new TabEntity(mTitles[i], mIconSelectIds[i], mIconUnselectIds[i]));
}
//实现点击tab,页面切换
commonTabLayout.setOnTabSelectListener(new OnTabSelectListener() {
//@position frag中集合的下标
@Override
public void onTabSelect(int position) {
//设置分页的当前页
viewPager.setCurrentItem(position);
}
@Override
public void onTabReselect(int position) {
}
});
//为tab视图对象,设置tab集合
commonTabLayout.setTabData(mTabEntities);
//为pager添加上述内容
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), mTitles, mFragments));
//导航栏对应切换
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
//当页面切换时,传入当前页面坐标,让导航栏对应地切换
@Override
public void onPageSelected(int position) {
commonTabLayout.setCurrentTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
}
package com.ttit.myapp.activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.google.gson.Gson;
import com.ttit.myapp.R;
import com.ttit.myapp.api.Api;
import com.ttit.myapp.api.ApiConfig;
import com.ttit.myapp.api.TtitCallback;
import com.ttit.myapp.entity.LoginResponse;
import com.ttit.myapp.util.AppConfig;
import com.ttit.myapp.util.StringUtils;
import org.json.JSONObject;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class LoginActivity extends BaseActivity {
private Button btnLogin;
private EditText etAccount;
private EditText etPwd;
@Override
protected int initLayout() {
return R.layout.activity_login;
}
@Override
protected void initView() {
etAccount = findViewById(R.id.et_account);
etPwd = findViewById(R.id.et_pwd);
btnLogin = findViewById(R.id.btn_login);
}
@Override
protected void initData() {
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String account = etAccount.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();
login(account, pwd);
}
});
}
//登录验证
private void login(String account, String pwd) {
if (StringUtils.isEmpty(account)) {
showToast("请输入账号");
return;
}
if (StringUtils.isEmpty(pwd)) {
showToast("请输入密码");
return;
}
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("mobile", account);
params.put("password", pwd);
Api.config(ApiConfig.LOGIN, params).postRequest(this,new TtitCallback() {
@Override
public void onSuccess(final String res) {
Log.e("onSuccess", res);
//json格式字符串转化为类对象
Gson gson = new Gson();
LoginResponse loginResponse = gson.fromJson(res, LoginResponse.class);
if (loginResponse.getCode() == 0) {
String token = loginResponse.getToken();
insertVal("token", token);
navigateTo(HomeActivity.class);//若写在83行,则因为线程原因不会执行该代码。
showToastSync("登录成功");
} else {
showToastSync("登录失败");
}
}
@Override
public void onFailure(Exception e) {
navigateTo(HomeActivity.class);
}
});
}
// private void login(String account, String pwd) {
// if (StringUtils.isEmpty(account)) {
// showToast("请输入账号");
// return;
// }
// if (StringUtils.isEmpty(pwd)) {
// showToast("请输入密码");
// return;
// }
// // 第一步创建OKHttpClient
// OkHttpClient client = new OkHttpClient.Builder()
// .build();
// Map m = new HashMap();
// m.put("mobile", account);
// m.put("password", pwd);
// JSONObject jsonObject = new JSONObject(m);
// String jsonStr = jsonObject.toString();
// RequestBody requestBodyJson =
// RequestBody.create(MediaType.parse("application/json;charset=utf-8")
// , jsonStr);
// //第三步创建Rquest
// Request request = new Request.Builder()
// .url(AppConfig.BASE_URl + "/app/login")
// .addHeader("contentType", "application/json;charset=UTF-8")
// .post(requestBodyJson)
// .build();
// //第四步创建call回调对象
// final Call call = client.newCall(request);
// //第五步发起请求
// call.enqueue(new Callback() {
// @Override
// public void onFailure(Call call, IOException e) {
// Log.e("onFailure", e.getMessage());
// }
//
// @Override
// public void onResponse(Call call, Response response) throws IOException {
// final String result = response.body().string();
// //ui必须在主线程处理 //原因:若在子线程处理会报运行时错误
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// showToast(result);
// }
// });
// }
// });
// }
}
package com.ttit.myapp.activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.ttit.myapp.R;
import com.ttit.myapp.api.Api;
import com.ttit.myapp.api.ApiConfig;
import com.ttit.myapp.api.TtitCallback;
import com.ttit.myapp.util.StringUtils;
import java.util.HashMap;
public class RegisterActivity extends BaseActivity {
private Button btnRegister;
private EditText etAccount;
private EditText etPwd;
@Override
protected int initLayout() {
return R.layout.activity_register;
}
@Override
protected void initView() {
etAccount = findViewById(R.id.et_account);
etPwd = findViewById(R.id.et_pwd);
btnRegister = findViewById(R.id.btn_register);
}
@Override
protected void initData() {
btnRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String account = etAccount.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();
register(account, pwd);
}
});
}
//注册验证
private void register(String account, String pwd) {
if (StringUtils.isEmpty(account)) {
showToast("请输入账号");
return;
}
if (StringUtils.isEmpty(pwd)) {
showToast("请输入密码");
return;
}
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("mobile", account);
params.put("password", pwd);
Api.config(ApiConfig.REGISTER, params).postRequest(this,new TtitCallback() {
@Override
public void onSuccess(final String res) {
runOnUiThread(new Runnable() {
@Override
public void run() {
showToast(res);
}
});
}
@Override
public void onFailure(Exception e) {
Log.e("onFailure", e.toString());
}
});
}
}
package com.ttit.myapp;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.ttit.myapp.activity.BaseActivity;
import com.ttit.myapp.activity.LoginActivity;
import com.ttit.myapp.activity.RegisterActivity;
public class MainActivity extends BaseActivity {
private Button btnLogin;
private Button btnRegister;
@Override
protected int initLayout() {
return R.layout.activity_main;
}
@Override
protected void initView() {
btnLogin = findViewById(R.id.btn_login);
btnRegister = findViewById(R.id.btn_register);
}
@Override
protected void initData() {
//登录按钮
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Intent intent = new Intent(MainActivity.this, LoginActivity.class);
// startActivity(intent);
navigateTo(LoginActivity.class);
}
});
//注册按钮
btnRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Intent intent = new Intent(MainActivity.this, RegisterActivity.class);
// startActivity(intent);
navigateTo(RegisterActivity.class);
}
});
}
}
根据标题创建对应frag
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tl="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".fragment.HomeFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="115dp"
android:background="#344261"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="39dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="26dp"
android:layout_marginRight="24dp"
android:background="@drawable/shape_search_box"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginLeft="13dp"
android:src="@mipmap/search" />
<EditText
android:id="@+id/et_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="11dp"
android:background="@null"
android:hint="搜索你喜欢的视频"
android:textColor="@color/black"
android:textColorHint="#737373"
android:textSize="15sp" />
</LinearLayout>
<com.flyco.tablayout.SlidingTabLayout
android:id="@+id/slidingTabLayout"
android:layout_width="match_parent"
android:layout_height="50dp"
tl:tl_indicator_corner_radius="1.5dp"
tl:tl_indicator_height="3dp"
tl:tl_indicator_width="17dp"
tl:tl_textSelectColor="#fdf299"
tl:tl_textUnselectColor="#ffffff"
tl:tl_indicator_color="#fdf299"
tl:tl_textsize="16sp" />
</LinearLayout>
<com.ttit.myapp.view.FixedViewPager
android:id="@+id/fixedViewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
//两个控件:滑动导航、tab导航
fragment_video.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".fragment.VideoFragment">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/black"
android:textSize="30sp"/>
FrameLayout>
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import com.flyco.tablayout.SlidingTabLayout;
import com.ttit.myapp.R;
import com.ttit.myapp.adapter.HomeAdapter;
import java.util.ArrayList;
public class HomeFragment extends Fragment {
private ArrayList<Fragment> mFragments = new ArrayList<>();
private final String[] mTitles = {
"热门", "ios", "我的",
"我的", "我的", "我的", "我的"
};
private ViewPager viewPager;
private SlidingTabLayout slidingTabLayout;
public static HomeFragment newInstance (){
HomeFragment fragment = new HomeFragment();
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_home, container, false);//转为布局文件为对象
//1 绑定导航栏和viewpager
viewPager = v.findViewById(R.id.fixedViewPager);
slidingTabLayout = v.findViewById(R.id.slidingTabLayout);
return v;
}
//重写,当onCreateView执行完毕后再执行
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//根据导航栏标题数量创建frag,传入对应的导航栏标题
for (String title : mTitles) {
mFragments.add(VideoFragment.newInstance(title));
}
//2 为viewpager设置适配器,该适配器包含frag
viewPager.setAdapter(new HomeAdapter(getFragmentManager(), mTitles, mFragments));
//3 绑定导航栏和viewpager,实现滑动效果
slidingTabLayout.setViewPager(viewPager);
}
}
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ttit.myapp.R;
public class VideoFragment extends Fragment {
private String title;//导航栏文字
public static VideoFragment newInstance (String title){
VideoFragment fragment = new VideoFragment();
//接收传入的title
fragment.title = title;
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_video, container, false);//转为布局文件为对象
//设置导航栏文字,根据传入的title
TextView tv = v.findViewById(R.id.title);
tv.setText(title);
return v;
}
}
package com.ttit.myapp.adapter;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.ArrayList;
//用于设置pager的页面
public class HomeAdapter extends FragmentPagerAdapter {
private String[] mTitles;
private ArrayList<Fragment> mFragments;
//通过构造器传入页面属性,以设置
public HomeAdapter(FragmentManager fm, String[] titles, ArrayList<Fragment> fragments) {
super(fm);
this.mTitles = titles;
this.mFragments = fragments;
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
}
package com.ttit.myapp.view;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;
public class FixedViewPager extends ViewPager {
public FixedViewPager(@NonNull Context context) {
super(context);
}
public FixedViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setCurrentItem(int item) {
super.setCurrentItem(item, false);
}
}
// 语法-预加载fragment:
//解决闪退问题-预加载
viewPager.setOffscreenPageLimit(mFragments.size());
HomeActivity
@Override
protected void initData() {
//添加fra实例化对象入集合
mFragments.add(HomeFragment.newInstance());
mFragments.add(CollectFragment.newInstance());
mFragments.add(MyFragment.newInstance());
//添加tab实例化对象入集合
for (int i = 0; i < mTitles.length; i++) {
mTabEntities.add(new TabEntity(mTitles[i], mIconSelectIds[i], mIconUnselectIds[i]));
}
//实现点击tab,页面切换
commonTabLayout.setOnTabSelectListener(new OnTabSelectListener() {
//@position frag中集合的下标
@Override
public void onTabSelect(int position) {
//设置分页的当前页
viewPager.setCurrentItem(position);
}
@Override
public void onTabReselect(int position) {
}
});
//为tab视图对象,设置tab集合
commonTabLayout.setTabData(mTabEntities);
//为pager添加上述内容
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), mTitles, mFragments));
//解决闪退问题-预加载
viewPager.setOffscreenPageLimit(mFragments.size());
//导航栏对应切换
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
//当页面切换时,传入当前页面坐标,让导航栏对应地切换
@Override
public void onPageSelected(int position) {
commonTabLayout.setCurrentTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
HomeFragment
//重写,当onCreateView执行完毕后再执行
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//根据导航栏标题数量创建frag,传入对应的导航栏标题
for (String title : mTitles) {
mFragments.add(VideoFragment.newInstance(title));
}
//解决闪退问题-预加载
viewPager.setOffscreenPageLimit(mFragments.size());
//2 为viewpager设置适配器,该适配器包含frag
viewPager.setAdapter(new HomeAdapter(getFragmentManager(), mTitles, mFragments));
//3 绑定导航栏和viewpager,实现滑动效果
slidingTabLayout.setViewPager(viewPager);
}
1.安装mysql8 || mysql5.7
2.设置密码和账号
root 123456
3.使用navicat工具导入myapp.sql文件
4.使用java命令运行Tomcat
java -jar renren-fast.jar
5.修改请求地址为本地地址
若使用5.7版本:
在该文件夹下存放jar包资源
在该文件夹路径下启动下面命令
java -jar renren-fast.jar
启动成功:未终止服务:
package com.ttit.myapp.api;
public class ApiConfig {
//本地服务器地址 1本地IP地址
public static final String BASE_URl = "http://127.0.0.1:8080/renren-fast";
public static final String LOGIN = "/app/login"; //登录
public static final String REGISTER = "/app/register";//注册
}
未联网,修改请求地址为本地地址127.0.0.1:8080
若联网,则本地地址不是这个,而是:
1 E/onFailure: socket failed: EPERM (Operation not permitted)
2 无法连接到请求地址:
3 网络超时:
<LinearLayout 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="@color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="21dp"
android:paddingTop="13dp"
android:paddingRight="21dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="42dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_header"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@mipmap/header" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="11dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="韭菜盒子新做法,不发面不烫面"
android:textColor="#242424"
android:textSize="14sp" />
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="大胃王"
android:textColor="#9f9f9f"
android:textSize="12sp" />
RelativeLayout>
LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="187dp"
android:layout_marginTop="8dp">
<ImageView
android:id="@+id/img_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@mipmap/default_bg" />
RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="39dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_comment"
android:layout_width="19dp"
android:layout_height="19dp"
android:src="@mipmap/comment" />
<TextView
android:id="@+id/comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:text="0"
android:textColor="#161616"
android:textSize="14sp" />
LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_centerVertical="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_collect"
android:layout_width="19dp"
android:layout_height="19dp"
android:src="@mipmap/collect" />
<TextView
android:id="@+id/collect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:text="0"
android:textColor="#161616"
android:textSize="14sp" />
LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_like"
android:layout_width="19dp"
android:layout_height="19dp"
android:src="@mipmap/dianzan" />
<TextView
android:id="@+id/dz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:text="0"
android:textColor="#161616"
android:textSize="14sp" />
LinearLayout>
RelativeLayout>
LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="7dp"
android:background="#f5f5f4" />
LinearLayout>
implementation 'androidx.recyclerview:recyclerview:1.2.0'
// 目的:为了使用列表控件
fragment_video.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".fragment.VideoFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
LinearLayout>
com.ttit.myapp.entity.VideoEntity.java
package com.ttit.myapp.entity;
import java.io.Serializable;
/**
* @author: wuzhideren
* @date: 2022
**/
public class VideoEntity implements Serializable {
private int id;
private String title; //标题
private String name; // 视频名字
private int dzCount; // 点赞数量
private int collectCount; //评论数量
private int commentCount; // 收藏数量
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDzCount() {
return dzCount;
}
public void setDzCount(int dzCount) {
this.dzCount = dzCount;
}
public int getCollectCount() {
return collectCount;
}
public void setCollectCount(int collectCount) {
this.collectCount = collectCount;
}
public int getCommentCount() {
return commentCount;
}
public void setCommentCount(int commentCount) {
this.commentCount = commentCount;
}
}
/**
* @author wuzhideren
*/
package com.ttit.myapp.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.ttit.myapp.R;
import com.ttit.myapp.entity.VideoEntity;
import org.w3c.dom.Text;
import java.util.List;
public class VideoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private List<VideoEntity> datas;
//1 构造时,接收
public VideoAdapter(Context context, List<VideoEntity> datas){
this.mContext = context;
this.datas = datas;
}
//2 设置每一项的布局
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 获取视图对象
View view = LayoutInflater.from(mContext).inflate(R.layout.item_video_layout, parent, false); // @parent 视图对象集
ViewHolder viewHolder = new ViewHolder(view);
// 返回视图控件
return viewHolder;
}
// 4 给视图对象赋值 1 当前对象 2 当前下标,作用:获取当前数据公共类对象
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
// 获取视图对象
ViewHolder vh = (ViewHolder)holder; // 记得强转
// 获取数据
VideoEntity videoEntity = datas.get(position);
// 设置数据
vh.tvTitle.setText(videoEntity.getTitle());
vh.tvAuthor.setText(videoEntity.getName());
vh.tvDz.setText(String.valueOf(videoEntity.getDzCount()));// 需要转换为字符串
vh.tvCollect.setText(String.valueOf(videoEntity.getCollectCount()));
vh.tvComment.setText(String.valueOf(videoEntity.getCommentCount()));
}
// 2 设置需要加载模块的数量
@Override
public int getItemCount() {
return datas.size();// 根据传入数据,决定加载的数量。
}
// 3 获取视图对象
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView tvTitle;
private TextView tvAuthor;
private TextView tvDz;
private TextView tvCollect;
private TextView tvComment;
// 传入布局对象
public ViewHolder(@NonNull View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.title);
tvAuthor = itemView.findViewById(R.id.author);
tvDz = itemView.findViewById(R.id.dz);
tvCollect = itemView.findViewById(R.id.collect);
tvComment = itemView.findViewById(R.id.comment);
}
}
}
位置:com/ttit/myapp/adapter/VideoAdapter.java
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_video, container, false);//转为布局文件为对象
// //设置导航栏文字,根据传入的title
// TextView tv = v.findViewById(R.id.title);
// tv.setText(title);
// 配置列表布局
RecyclerView recyclerView = v.findViewById(R.id.recyclerView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());// 获取父活动对象
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);// 设置为垂直排列
recyclerView.setLayoutManager(linearLayoutManager);// 为视图对象,设置布局管理器
// 传入数据
// 设置数据
List<VideoEntity> datas = new ArrayList<>();
for (int i = 0; i < 8; i++) {
VideoEntity videoEntity = new VideoEntity();
videoEntity.setTitle("韭菜盒子");
videoEntity.setName("韭菜盒子");
videoEntity.setDzCount(i * 2);
videoEntity.setCollectCount(i * 4);
videoEntity.setCommentCount(i * 6);
datas.add(videoEntity);
}
// 传入数据
VideoAdapter videoAdapter = new VideoAdapter(getActivity(), datas);
// 添加布局到 主页列表控件
recyclerView.setAdapter(videoAdapter);
return v;
}
com/ttit/myapp/api/Api.java
public void getRequest(final TtitCallback callback) {
String url = getAppendUrl(requestUrl, mParams);
Request request = new Request.Builder()
.url(url)
.get()
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("onFailure", e.getMessage());
callback.onFailure(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String result = response.body().string();
callback.onSuccess(result);
}
});
}
public void getRequest(Context context, final TtitCallback callback) {
SharedPreferences sp = context.getSharedPreferences("sp_ttit", MODE_PRIVATE);
String token = sp.getString("token", "");
String url = getAppendUrl(requestUrl, mParams);
Request request = new Request.Builder()
.url(url)
.addHeader("token", token)
.get()
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("onFailure", e.getMessage());
callback.onFailure(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String result = response.body().string();
try {
JSONObject jsonObject = new JSONObject(result);
String code = jsonObject.getString("code");
if (code.equals("401")) {
Intent in = new Intent(context, LoginActivity.class);
context.startActivity(in);
}
} catch (JSONException e) {
e.printStackTrace();
}
callback.onSuccess(result);
}
});
}
private String getAppendUrl(String url, Map<String, Object> map) {
if (map != null && !map.isEmpty()) {
StringBuffer buffer = new StringBuffer();
Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, Object> entry = iterator.next();
if (StringUtils.isEmpty(buffer.toString())) {
buffer.append("?");
} else {
buffer.append("&");
}
buffer.append(entry.getKey()).append("=").append(entry.getValue());
}
url += buffer.toString();
}
return url;
}
com.ttit.myapp.api.ApiConfig
public static final String VIDEO_LIST = "/app/videolist/list";//所有类型视频列表
com.ttit.myapp.fragment.BaseFragment
复制BaseActivity内容,修改上下文对象为getActivity()
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import static android.content.Context.MODE_PRIVATE;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Looper;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
public class BaseFragment extends Fragment {
//提示信息
public void showToast(String msg) {
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
}
//处理子线程ui
public void showToastSync(String msg) {
//令子线程可以有ui显示,而不报错
Looper.prepare();
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
Looper.loop();//易错修正:其后代码不会立即执行
}
//活动跳转
public void navigateTo(Class cls) {
Intent in = new Intent(getActivity(), cls);
startActivity(in);
}
//sp存储
protected void insertVal(String key, String val) {
SharedPreferences sp = getActivity().getSharedPreferences("sp_ttit", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString(key, val);
editor.commit();
}
//取出sq数据
protected String getStringFromSp(String key){
SharedPreferences sp = getActivity().getSharedPreferences("sp_ttit", MODE_PRIVATE);
return sp.getString(key,"");
}
}
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
import com.ttit.myapp.R;
import com.ttit.myapp.activity.LoginActivity;
import com.ttit.myapp.adapter.VideoAdapter;
import com.ttit.myapp.api.Api;
import com.ttit.myapp.api.ApiConfig;
import com.ttit.myapp.api.TtitCallback;
import com.ttit.myapp.entity.VideoEntity;
import com.ttit.myapp.entity.VideoListResponse;
import com.ttit.myapp.util.StringUtils;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
public class VideoFragment extends BaseFragment {
private String title;//导航栏文字
private RecyclerView recyclerView;
private RefreshLayout refreshLayout;
public static VideoFragment newInstance (String title){
VideoFragment fragment = new VideoFragment();
//接收传入的title
fragment.title = title;
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_video, container, false);//转为布局文件为对象
// //设置导航栏文字,根据传入的title
// TextView tv = v.findViewById(R.id.title);
// tv.setText(title);
// 配置列表布局
recyclerView = v.findViewById(R.id.recyclerView);
refreshLayout = v.findViewById(R.id.refreshLayout);
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {
refreshlayout.finishRefresh(2000/*,false*/);//传入false表示刷新失败
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
refreshlayout.finishLoadMore(2000/*,false*/);//传入false表示加载失败
}
});
return v;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());// 获取父活动对象
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);// 设置为垂直排列
recyclerView.setLayoutManager(linearLayoutManager);// 为视图对象,设置布局管理器
// 传入数据
// 设置数据
// List datas = new ArrayList<>();
// for (int i = 0; i < 8; i++) {
// VideoEntity videoEntity = new VideoEntity();
// videoEntity.setTitle("韭菜盒子");
// videoEntity.setName("韭菜盒子");
// videoEntity.setDzCount(i * 2);
// videoEntity.setCollectCount(i * 4);
// videoEntity.setCommentCount(i * 6);
// datas.add(videoEntity);
// }
getVideoList();
}
//获取视频列表数据
private void getVideoList() {
String token = getStringFromSp("token");
//如果登录账户不为空
if (!StringUtils.isEmpty(token)){
HashMap<String, Object> params = new HashMap<>();//注意:是String
params.put("token", token);
Api.config(ApiConfig.VIDEO_LIST, params).getRequest(new TtitCallback(){
@Override
public void onSuccess(String res) {
VideoListResponse response = new Gson().fromJson(res, VideoListResponse.class);
if (response != null && response.getCode() == 0) {
List<VideoEntity> datas = response.getPage().getList();
// 传入数据
VideoAdapter videoAdapter = new VideoAdapter(getActivity(), datas);
// 添加布局到 主页列表控件
recyclerView.setAdapter(videoAdapter);
// showToastSync(res);
}
}
@Override
public void onFailure(Exception e) {
}
});
}else {//说明没有登录
navigateTo(LoginActivity.class);
}
}
}
package com.ttit.myapp.entity;
import java.io.Serializable;
import java.util.List;
/**
* @author: wuzhi
* @date: 2022
**/
public class VideoListResponse implements Serializable {
/**
* msg : success
* code : 0
* page : {"totalCount":4,"pageSize":10,"totalPage":1,"currPage":1,"list":[{"vid":1,"vtitle":"青龙战甲搭配机动兵,P城上空肆意1V4","author":"狙击手麦克","coverurl":"https://sf3-xgcdn-tos.pstatp.com/img/tos-cn-i-0004/527d013205a74eb0a77202d7a9d5b511~tplv-crop-center:1041:582.jpg","headurl":"https://sf1-ttcdn-tos.pstatp.com/img/pgc-image/c783a73368fa4666b7842a635c63a8bf~360x360.image","commentNum":210,"likeNum":23,"collectNum":100},{"vid":2,"vtitle":"【仁王2】视频攻略 2-3 虚幻魔城","author":"黑桐谷歌","coverurl":"https://lf1-xgcdn-tos.pstatp.com/img/tos-cn-p-0000/9ff7fe6c89e44ca3a22aad5744e569e3~tplv-crop-center:1041:582.jpg","headurl":"https://sf6-ttcdn-tos.pstatp.com/img/mosaic-legacy/8110/752553978~360x360.image","commentNum":1300,"likeNum":500,"collectNum":120},{"vid":3,"vtitle":"最猛暴击吕布教学,这才是战神该有的样子","author":"小凡解说游戏","coverurl":"https://sf1-xgcdn-tos.pstatp.com/img/tos-cn-i-0004/83cc11d5e26047c6b0ead149f41a8266~tplv-crop-center:1041:582.jpg","headurl":"https://p3.pstatp.com/large/a14a000405f16e51842f","commentNum":10,"likeNum":19,"collectNum":5},{"vid":4,"vtitle":"拳皇14:小孩输掉一分,印尼选手得意忘形","author":"E游未尽小E","coverurl":"https://sf1-xgcdn-tos.pstatp.com/img/tos-cn-i-0004/b9553b7a28d94f27a7115157797b52ff~tplv-crop-center:1041:582.jpg","headurl":"https://sf3-ttcdn-tos.pstatp.com/img/pgc-image/f6b840d23f9e465bb5ac9e570b28321d~360x360.image","commentNum":22,"likeNum":180,"collectNum":963}]}
*/
private String msg;
private int code;
private PageBean page;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public PageBean getPage() {
return page;
}
public void setPage(PageBean page) {
this.page = page;
}
public static class PageBean {
/**
* totalCount : 4
* pageSize : 10
* totalPage : 1
* currPage : 1
* list : [{"vid":1,"vtitle":"青龙战甲搭配机动兵,P城上空肆意1V4","author":"狙击手麦克","coverurl":"https://sf3-xgcdn-tos.pstatp.com/img/tos-cn-i-0004/527d013205a74eb0a77202d7a9d5b511~tplv-crop-center:1041:582.jpg","headurl":"https://sf1-ttcdn-tos.pstatp.com/img/pgc-image/c783a73368fa4666b7842a635c63a8bf~360x360.image","commentNum":210,"likeNum":23,"collectNum":100},{"vid":2,"vtitle":"【仁王2】视频攻略 2-3 虚幻魔城","author":"黑桐谷歌","coverurl":"https://lf1-xgcdn-tos.pstatp.com/img/tos-cn-p-0000/9ff7fe6c89e44ca3a22aad5744e569e3~tplv-crop-center:1041:582.jpg","headurl":"https://sf6-ttcdn-tos.pstatp.com/img/mosaic-legacy/8110/752553978~360x360.image","commentNum":1300,"likeNum":500,"collectNum":120},{"vid":3,"vtitle":"最猛暴击吕布教学,这才是战神该有的样子","author":"小凡解说游戏","coverurl":"https://sf1-xgcdn-tos.pstatp.com/img/tos-cn-i-0004/83cc11d5e26047c6b0ead149f41a8266~tplv-crop-center:1041:582.jpg","headurl":"https://p3.pstatp.com/large/a14a000405f16e51842f","commentNum":10,"likeNum":19,"collectNum":5},{"vid":4,"vtitle":"拳皇14:小孩输掉一分,印尼选手得意忘形","author":"E游未尽小E","coverurl":"https://sf1-xgcdn-tos.pstatp.com/img/tos-cn-i-0004/b9553b7a28d94f27a7115157797b52ff~tplv-crop-center:1041:582.jpg","headurl":"https://sf3-ttcdn-tos.pstatp.com/img/pgc-image/f6b840d23f9e465bb5ac9e570b28321d~360x360.image","commentNum":22,"likeNum":180,"collectNum":963}]
*/
private int totalCount;
private int pageSize;
private int totalPage;
private int currPage;
private List<VideoEntity> list;
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getCurrPage() {
return currPage;
}
public void setCurrPage(int currPage) {
this.currPage = currPage;
}
public List<VideoEntity> getList() {
return list;
}
public void setList(List<VideoEntity> list) {
this.list = list;
}
}
}
package com.ttit.myapp.entity;
import java.io.Serializable;
/**
* @author: wuzhideren
* @date: 2022
**/
public class VideoEntity implements Serializable {
/**
* vid : 1
* vtitle : 青龙战甲搭配机动兵,P城上空肆意1V4
* author : 狙击手麦克
* coverurl : http://sf3-xgcdn-tos.pstatp.com/img/tos-cn-i-0004/527d013205a74eb0a77202d7a9d5b511~tplv-crop-center:1041:582.jpg
* headurl : https://sf1-ttcdn-tos.pstatp.com/img/pgc-image/c783a73368fa4666b7842a635c63a8bf~360x360.image
* playurl : http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4
* createTime : 2020-07-14 11:21:45
* updateTime : 2020-07-19 12:05:33
* categoryId : 1
* categoryName : 游戏
* videoSocialEntity : {"commentnum":103,"likenum":121,"collectnum":220}
*/
private int vid;
private String vtitle;
private String author;
private String coverurl;
private String headurl;
private String commentnum;
private String likenum;
private String collectnum;
public int getVid() {
return vid;
}
public void setVid(int vid) {
this.vid = vid;
}
public String getVtitle() {
return vtitle;
}
public void setVtitle(String vtitle) {
this.vtitle = vtitle;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getCoverurl() {
return coverurl;
}
public void setCoverurl(String coverurl) {
this.coverurl = coverurl;
}
public String getHeadurl() {
return headurl;
}
public void setHeadurl(String headurl) {
this.headurl = headurl;
}
public String getCommentnum() {
return commentnum;
}
public void setCommentnum(String commentnum) {
this.commentnum = commentnum;
}
public String getLikenum() {
return likenum;
}
public void setLikenum(String likenum) {
this.likenum = likenum;
}
public String getCollectnum() {
return collectnum;
}
public void setCollectnum(String collectnum) {
this.collectnum = collectnum;
}
}
com.ttit.myapp.fragment.VideoFragment
修改代码
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
// 获取视图对象
ViewHolder vh = (ViewHolder)holder; // 记得强转
// 获取数据
VideoEntity videoEntity = datas.get(position);
// 设置数据
vh.tvTitle.setText(videoEntity.getVtitle());
vh.tvAuthor.setText(videoEntity.getAuthor());
vh.tvDz.setText(String.valueOf(videoEntity.getVideoSocialEntity().getLikenum()));// 需要转换为字符串
vh.tvCollect.setText(String.valueOf(videoEntity.getVideoSocialEntity().getCollectnum()));
vh.tvComment.setText(String.valueOf(videoEntity.getVideoSocialEntity().getCommentnum()));
目的:异步加载图片,加载网络图片资源
implementation 'com.squareup.picasso:picasso:2.5.2'
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
// 获取视图对象
ViewHolder vh = (ViewHolder)holder; // 记得强转
// 获取数据
VideoEntity videoEntity = datas.get(position);
// 设置数据
vh.tvTitle.setText(videoEntity.getVtitle());
vh.tvAuthor.setText(videoEntity.getAuthor());
vh.tvDz.setText(String.valueOf(videoEntity.getVideoSocialEntity().getLikenum()));// 需要转换为字符串
vh.tvCollect.setText(String.valueOf(videoEntity.getVideoSocialEntity().getCollectnum()));
vh.tvComment.setText(String.valueOf(videoEntity.getVideoSocialEntity().getCommentnum()));
// 异步加载网络图片
Picasso.with(mContext).load(videoEntity.getHeadurl()).into(vh.imgHeader);
Picasso.with(mContext).load(videoEntity.getCoverurl()).into(vh.imgCover);
}
// 2 设置需要加载模块的数量
@Override
public int getItemCount() {
return datas.size();// 根据传入数据,决定加载的数量。
}
// 3 获取视图对象
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView tvTitle;
private TextView tvAuthor;
private TextView tvDz;
private TextView tvCollect;
private TextView tvComment;
// 获取图片资源
private ImageView imgHeader;
private ImageView imgCover;
// 传入布局对象
public ViewHolder(@NonNull View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.title);
tvAuthor = itemView.findViewById(R.id.author);
tvDz = itemView.findViewById(R.id.dz);
tvCollect = itemView.findViewById(R.id.collect);
tvComment = itemView.findViewById(R.id.comment);
imgHeader = itemView.findViewById(R.id.img_header);
imgCover = itemView.findViewById(R.id.img_cover);
}
}
利用毕加索插件,实现方法,重新绘制图片
package com.ttit.myapp.view;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.squareup.picasso.Transformation;
/**
* @author: wuzhideren
* @date: 2022
**/
public class CircleTransform implements Transformation {
@Override
public Bitmap transform(Bitmap source) {
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(squaredBitmap,
BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
paint.setShader(shader);
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
squaredBitmap.recycle();
return bitmap;
}
@Override
public String key() {
return "circle";
}
}
修改com.ttit.myapp.adapter.VideoAdapter
// 异步加载网络图片
Picasso.with(mContext)
.load(videoEntity.getHeadurl())
.transform(new CircleTransform())
.into(vh.imgHeader);
Picasso.with(mContext).load(videoEntity.getCoverurl())
.transform(new CircleTransform())
.into(vh.imgCover);
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.3' //1.0.5及以前版本的老用户升级需谨慎,API改动过大
https://github.com/scwang90/SmartRefreshLayout/tree/1.x#%E7%AE%80%E5%8D%95%E7%94%A8%E4%BE%8B
layout/fragment_video.xml
<com.scwang.smartrefresh.layout.SmartRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".fragment.VideoFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
com.scwang.smartrefresh.layout.SmartRefreshLayout>
com.ttit.myapp.fragment.VideoFragment
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_video, container, false);//转为布局文件为对象
// //设置导航栏文字,根据传入的title
// TextView tv = v.findViewById(R.id.title);
// tv.setText(title);
// 配置列表布局
recyclerView = v.findViewById(R.id.recyclerView);
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {
refreshlayout.finishRefresh(2000/*,false*/);//传入false表示刷新失败
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
refreshlayout.finishLoadMore(2000/*,false*/);//传入false表示加载失败
}
});
return v;
}
com.ttit.myapp.fragment.VideoFragment
//获取视频列表数据
private void getVideoList(boolean isRefresh) {//@isRefresh 分页-1 区分刷新还是下拉加载
String token = getStringFromSp("token");
//如果登录账户不为空
if (!StringUtils.isEmpty(token)){
HashMap<String, Object> params = new HashMap<>();//注意:是String,这是请求数据的容器
params.put("token", token);
params.put("page", pageNum);// 总页数
params.put("limit", ApiConfig.PAGE_SIZE);// 每页数量
if (response != null && response.getCode() == 0) { // 如果返回数据正常
List<VideoEntity> list = response.getPage().getList(); // 获取格式化后返回数据
if (list != null && list.size() > 0){ // 如果有数据
if (isRefresh){// 如果时刷新
datas = list;
} else { // 如果加载
datas.addAll(list); // 追加加载后的数据
}
videoAdapter.setDatas(datas);
videoAdapter.notifyDataSetChanged();// 通知刷新数据
}else {
if (isRefresh){
showToastSync("暂时无数据");
}else {
showToastSync("没有更多数据");
}
}
}
com.ttit.myapp.api.ApiConfig
// 接收传入数据
public void setDatas(List<VideoEntity> datas) {
this.datas = datas;
}
//1 构造时,不接收
public VideoAdapter(Context context){
this.mContext = context;
}
//1 构造时,接收
public VideoAdapter(Context context, List<VideoEntity> datas){
this.mContext = context;
this.datas = datas;
}
解决没有数据时的报错
@Override
public int getItemCount() {
if (datas != null){
return datas.size();// 根据传入数据,决定加载的数量。
}
return 0;
}
这是完整代码
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
import com.ttit.myapp.R;
import com.ttit.myapp.activity.LoginActivity;
import com.ttit.myapp.adapter.VideoAdapter;
import com.ttit.myapp.api.Api;
import com.ttit.myapp.api.ApiConfig;
import com.ttit.myapp.api.TtitCallback;
import com.ttit.myapp.entity.VideoEntity;
import com.ttit.myapp.entity.VideoListResponse;
import com.ttit.myapp.util.StringUtils;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
public class VideoFragment extends BaseFragment {
private String title;//导航栏文字
private RecyclerView recyclerView;
private RefreshLayout refreshLayout;
private int pageNum = 1; // 页数
List<VideoEntity> datas = new ArrayList<>(); // 传入的数据
VideoAdapter videoAdapter;
public static VideoFragment newInstance (String title){
VideoFragment fragment = new VideoFragment();
//接收传入的title
fragment.title = title;
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_video, container, false);//转为布局文件为对象
// //设置导航栏文字,根据传入的title
// TextView tv = v.findViewById(R.id.title);
// tv.setText(title);
// 配置列表布局
recyclerView = v.findViewById(R.id.recyclerView);
refreshLayout = v.findViewById(R.id.refreshLayout);
return v;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());// 获取父活动对象
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);// 设置为垂直排列
recyclerView.setLayoutManager(linearLayoutManager);// 为视图对象,设置布局管理器
// 创建设配器
videoAdapter = new VideoAdapter(getActivity());
// 添加布局到 主页列表控件
recyclerView.setAdapter(videoAdapter);
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {// 过程:下拉刷新时,触发该方法
// refreshlayout.finishRefresh(2000/*,false*/);//传入false表示刷新失败
pageNum = 1; // 每次刷新 页数重置为1,就是回到首页
getVideoList(true); // 传入数据
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
// refreshlayout.finishLoadMore(2000/*,false*/);//传入false表示加载失败
pageNum ++; // 每加载一次,下一页
getVideoList(false);// 更新传入的数据
}
});
getVideoList(true);// 分页-2 第一次进来刷新
}
//获取视频列表数据
private void getVideoList(boolean isRefresh) {//@isRefresh 分页-1 区分刷新还是下拉加载
String token = getStringFromSp("token");
//如果登录账户不为空
if (!StringUtils.isEmpty(token)){
HashMap<String, Object> params = new HashMap<>();//注意:是String,这是请求数据的容器
params.put("token", token);
params.put("page", pageNum);// 总页数
params.put("limit", ApiConfig.PAGE_SIZE);// 每页数量
Api.config(ApiConfig.VIDEO_LIST, params).getRequest(new TtitCallback(){
@Override
public void onSuccess(String res) {
getActivity().runOnUiThread(new Runnable() {//解决子线程无法加载ui问题
@Override
public void run() {
if (isRefresh){// 请求成功就关闭加载动画
refreshLayout.finishRefresh();
}else {
refreshLayout.finishLoadMore();// 注意!别和上面写成一样了
}
VideoListResponse response = new Gson().fromJson(res, VideoListResponse.class);
if (response != null && response.getCode() == 0) { // 如果返回数据正常
List<VideoEntity> list = response.getPage().getList(); // 获取格式化后返回数据
if (list != null && list.size() > 0){ // 如果有数据
if (isRefresh){// 如果时刷新
datas = list;
} else { // 如果加载
datas.addAll(list); // 追加加载后的数据
}
videoAdapter.setDatas(datas);
videoAdapter.notifyDataSetChanged();// 通知刷新数据
}else {
if (isRefresh){
// showToastSync();// 注意!不能使用
showToast("暂时无数据");
}else {
showToast("没有更多数据");
}
}
}
}
});
}
@Override
public void onFailure(Exception e) {
if (isRefresh){
refreshLayout.finishRefresh();
}else {
refreshLayout.finishLoadMore();
}
}
});
}else {//说明没有登录
navigateTo(LoginActivity.class);
}
}
}
com.ttit.myapp.fragment.BaseFragment
修改基类为抽象类,编写三个抽象方法,主要接收布局文件以加载
public abstract class BaseFragment extends Fragment {
protected View mRootView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(initLayout(), container, false);
initView();
}
return mRootView;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initData();
}
protected abstract int initLayout();
protected abstract void initView();
protected abstract void initData();
继承类重写抽象方法,整理代码,传入布局文件
@Override
protected int initLayout() {
return R.layout.fragment_video;
}
@Override
protected void initView() {
recyclerView = mRootView.findViewById(R.id.recyclerView);
refreshLayout = mRootView.findViewById(R.id.refreshLayout);
}
// 过程与onViewCreated一样
@Override
protected void initData() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());// 获取父活动对象
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);// 设置为垂直排列
recyclerView.setLayoutManager(linearLayoutManager);// 为视图对象,设置布局管理器
// 创建设配器
videoAdapter = new VideoAdapter(getActivity());
// 添加布局到 主页列表控件
recyclerView.setAdapter(videoAdapter);
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {// 过程:下拉刷新时,触发该方法
// refreshlayout.finishRefresh(2000/*,false*/);//传入false表示刷新失败
pageNum = 1; // 每次刷新 页数重置为1,就是回到首页
getVideoList(true); // 传入数据
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
// refreshlayout.finishLoadMore(2000/*,false*/);//传入false表示加载失败
pageNum ++; // 每加载一次,下一页
getVideoList(false);// 更新传入的数据
}
});
getVideoList(true);// 分页-2 第一次进来刷新
}
protected VideoView mVideoView;
protected StandardVideoController mController;
protected ErrorView mErrorView;
protected CompleteView mCompleteView;
protected TitleView mTitleView;
implementation 'com.github.dueeeke.dkplayer:dkplayer-java:3.2.6'
implementation 'com.github.dueeeke.dkplayer:dkplayer-ui:3.2.6'
implementation 'com.github.dueeeke.dkplayer:player-exo:3.2.6'
implementation 'com.github.dueeeke.dkplayer:player-ijk:3.2.6'
implementation 'com.github.dueeeke.dkplayer:videocache:3.2.6'
import android.widget.VideoView; // 注意:别错包!!!
// 正确:
import com.dueeeke.videoplayer.player.VideoView;
/**
* 当前播放的位置
*/
protected int mCurPos = -1;
/**
* 上次播放的位置,用于页面切回来之后恢复播放
*/
protected int mLastPos = mCurPos;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
videoAdapter.setDatas(datas);
videoAdapter.notifyDataSetChanged();
break;
}
}
};
// 视频视图信息配置
protected void initVideoView() {
mVideoView = new VideoView(getActivity());
mVideoView.setOnStateChangeListener(new com.dueeeke.videoplayer.player.VideoView.SimpleOnStateChangeListener() {
@Override
public void onPlayStateChanged(int playState) {
//监听VideoViewManager释放,重置状态
if (playState == com.dueeeke.videoplayer.player.VideoView.STATE_IDLE) {
Utils.removeViewFormParent(mVideoView);
mLastPos = mCurPos;
mCurPos = -1;
}
}
});
mController = new StandardVideoController(getActivity());
mErrorView = new ErrorView(getActivity());
mController.addControlComponent(mErrorView);
mCompleteView = new CompleteView(getActivity());
mController.addControlComponent(mCompleteView);
mTitleView = new TitleView(getActivity());
mController.addControlComponent(mTitleView);
mController.addControlComponent(new VodControlView(getActivity()));
mController.addControlComponent(new GestureView(getActivity()));
mController.setEnableOrientation(true);
mVideoView.setVideoController(mController);
}
recyclerView.setAdapter(videoAdapter);
recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
}
@Override
public void onChildViewDetachedFromWindow(@NonNull View view) {
FrameLayout playerContainer = view.findViewById(R.id.player_container);
View v = playerContainer.getChildAt(0);
if (v != null && v == mVideoView && !mVideoView.isFullScreen()) {
releaseVideoView();
}
}
});
@Override
public void onPause() {
super.onPause();
pause();
}
/**
* 由于onPause必须调用super。故增加此方法,
* 子类将会重写此方法,改变onPause的逻辑
*/
protected void pause() {
releaseVideoView();
}
@Override
public void onResume() {
super.onResume();
resume();
}
/**
* 由于onResume必须调用super。故增加此方法,
* 子类将会重写此方法,改变onResume的逻辑
*/
protected void resume() {
if (mLastPos == -1)
return;
//恢复上次播放的位置
startPlay(mLastPos);
}
/**
* PrepareView被点击
*/
@Override
public void onItemChildClick(int position) {
startPlay(position);
}
/**
* 开始播放
*
* @param position 列表位置
*/
protected void startPlay(int position) {
if (mCurPos == position) return;
if (mCurPos != -1) {
releaseVideoView();
}
VideoEntity videoEntity = datas.get(position);
//边播边存
// String proxyUrl = ProxyVideoCacheManager.getProxy(getActivity()).getProxyUrl(videoBean.getUrl());
// mVideoView.setUrl(proxyUrl);
mVideoView.setUrl(videoEntity.getPlayurl());
mTitleView.setTitle(videoEntity.getVtitle());
View itemView = linearLayoutManager.findViewByPosition(position);
if (itemView == null) return;
VideoAdapter.ViewHolder viewHolder = (VideoAdapter.ViewHolder) itemView.getTag();
//把列表中预置的PrepareView添加到控制器中,注意isPrivate此处只能为true。
mController.addControlComponent(viewHolder.mPrepareView, true);
Utils.removeViewFormParent(mVideoView);
viewHolder.mPlayerContainer.addView(mVideoView, 0);
//播放之前将VideoView添加到VideoViewManager以便在别的页面也能操作它
getVideoViewManager().add(mVideoView, Tag.LIST);
mVideoView.start();
mCurPos = position;
}
private void releaseVideoView() {
mVideoView.release();
if (mVideoView.isFullScreen()) {
mVideoView.stopFullScreen();
}
if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
mCurPos = -1;
}
package com.ttit.myapp.util;
import android.view.View;
import android.view.ViewParent;
import android.widget.FrameLayout;
import com.dueeeke.videoplayer.player.VideoView;
import com.dueeeke.videoplayer.player.VideoViewConfig;
import com.dueeeke.videoplayer.player.VideoViewManager;
import java.lang.reflect.Field;
public final class Utils {
private Utils() {
}
/**
* 获取当前的播放核心
*/
public static Object getCurrentPlayerFactory() {
VideoViewConfig config = VideoViewManager.getConfig();
Object playerFactory = null;
try {
Field mPlayerFactoryField = config.getClass().getDeclaredField("mPlayerFactory");
mPlayerFactoryField.setAccessible(true);
playerFactory = mPlayerFactoryField.get(config);
} catch (Exception e) {
e.printStackTrace();
}
return playerFactory;
}
/**
* 将View从父控件中移除
*/
public static void removeViewFormParent(View v) {
if (v == null) return;
ViewParent parent = v.getParent();
if (parent instanceof FrameLayout) {
((FrameLayout) parent).removeView(v);
}
}
/**
* Returns a string containing player state debugging information.
*/
public static String playState2str(int state) {
String playStateString;
switch (state) {
default:
case VideoView.STATE_IDLE:
playStateString = "idle";
break;
case VideoView.STATE_PREPARING:
playStateString = "preparing";
break;
case VideoView.STATE_PREPARED:
playStateString = "prepared";
break;
case VideoView.STATE_PLAYING:
playStateString = "playing";
break;
case VideoView.STATE_PAUSED:
playStateString = "pause";
break;
case VideoView.STATE_BUFFERING:
playStateString = "buffering";
break;
case VideoView.STATE_BUFFERED:
playStateString = "buffered";
break;
case VideoView.STATE_PLAYBACK_COMPLETED:
playStateString = "playback completed";
break;
case VideoView.STATE_ERROR:
playStateString = "error";
break;
}
return String.format("playState: %s", playStateString);
}
/**
* Returns a string containing player state debugging information.
*/
public static String playerState2str(int state) {
String playerStateString;
switch (state) {
default:
case VideoView.PLAYER_NORMAL:
playerStateString = "normal";
break;
case VideoView.PLAYER_FULL_SCREEN:
playerStateString = "full screen";
break;
case VideoView.PLAYER_TINY_SCREEN:
playerStateString = "tiny screen";
break;
}
return String.format("playerState: %s", playerStateString);
}
}
package com.ttit.myapp.util;
/**
* 播放器标签
*/
public final class Tag {
//列表播放
public static final String LIST = "list";
//无缝播放
public static final String SEAMLESS = "seamless";
//画中画
public static final String PIP = "pip";
}
item_video_layout.xml
<FrameLayout
android:id="@+id/player_container"
android:layout_width="match_parent"
android:layout_height="187dp"
android:layout_marginTop="8dp"
android:background="@android:color/black"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintTop_toTopOf="parent">
<com.dueeeke.videocontroller.component.PrepareView
android:id="@+id/prepare_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
FrameLayout>
xmlns:app="http://schemas.android.com/apk/res-auto"
为它添加set,get()
private String playurl;
public class ViewHolder extends RecyclerView.ViewHolder {
mPrepareView = itemView.findViewById(R.id.prepare_view);
mThumb = itemView.findViewById(R.id.thumb);
mPlayerContainer = itemView.findViewById(R.id.player_container);
protected VideoViewManager getVideoViewManager() {
return VideoViewManager.instance();
}
package com.ttit.myapp.listener;
public interface OnItemClickListener {
void onItemClick(int position);
}
package com.ttit.myapp.listener;
public interface OnItemChildClickListener {
void onItemChildClick(int position);
}
private OnItemChildClickListener mOnItemChildClickListener;
private OnItemClickListener mOnItemClickListener;
public class VideoFragment extends BaseFragment implements OnItemChildClickListener {
// 4 给视图对象赋值 1 当前对象 2 当前下标,作用:获取当前数据公共类对象
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
// 获取视图对象
ViewHolder vh = (ViewHolder) holder; // 记得强转
// 获取数据
VideoEntity videoEntity = datas.get(position);
// 设置数据
vh.tvTitle.setText(videoEntity.getVtitle());
vh.tvAuthor.setText(videoEntity.getAuthor());
vh.tvDz.setText(String.valueOf(videoEntity.getLikenum()));// 需要转换为字符串
vh.tvCollect.setText(String.valueOf(videoEntity.getCollectnum()));
vh.tvComment.setText(String.valueOf(videoEntity.getCommentnum()));
// 异步加载网络图片
Picasso.with(mContext)
.load(videoEntity.getHeadurl())
.transform(new CircleTransform())
.into(vh.imgHeader);
Picasso.with(mContext).load(videoEntity.getCoverurl())
.into(vh.mThumb);
vh.mPosition = holder.getAdapterPosition();
}
// 2 设置需要加载模块的数量
@Override
public int getItemCount() {
if (datas != null && datas.size() > 0) {
return datas.size();// 根据传入数据,决定加载的数量。
}
return 0;
}
// 3 获取视图对象
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView tvTitle;
private TextView tvAuthor;
private TextView tvDz;
private TextView tvCollect;
private TextView tvComment;
// 获取图片资源
private ImageView imgHeader;
// 获取视频控件容器
public ImageView mThumb;
public PrepareView mPrepareView;
public FrameLayout mPlayerContainer;
public int mPosition;
// 传入布局对象
public ViewHolder(@NonNull View view) {
super(view);
tvTitle = view.findViewById(R.id.title);
tvAuthor = view.findViewById(R.id.author);
tvDz = view.findViewById(R.id.dz);
tvCollect = view.findViewById(R.id.collect);
tvComment = view.findViewById(R.id.comment);
imgHeader = view.findViewById(R.id.img_header);
mPrepareView = view.findViewById(R.id.prepare_view);
mThumb = view.findViewById(R.id.thumb);
mPlayerContainer = view.findViewById(R.id.player_container);
mThumb = mPrepareView.findViewById(R.id.thumb);
if (mOnItemChildClickListener != null) {
mPlayerContainer.setOnClickListener(this);
}
if (mOnItemClickListener != null) {
itemView.setOnClickListener(this);
}
//通过tag将ViewHolder和itemView绑定
view.setTag(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.player_container) {
if (mOnItemChildClickListener != null) {
mOnItemChildClickListener.onItemChildClick(mPosition);
}
} else {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(mPosition);
}
}
}
}
public void setOnItemChildClickListener(OnItemChildClickListener onItemChildClickListener) {
mOnItemChildClickListener = onItemChildClickListener;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
@Override
protected void initData() {
linearLayoutManager = new LinearLayoutManager(getActivity());// 获取父活动对象
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);// 设置为垂直排列
recyclerView.setLayoutManager(linearLayoutManager);// 为视图对象,设置布局管理器
// 创建设配器
videoAdapter = new VideoAdapter(getActivity());
videoAdapter.setOnItemChildClickListener(this);
// 添加布局到 主页列表控件
recyclerView.setAdapter(videoAdapter);
recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
}
@Override
public void onChildViewDetachedFromWindow(@NonNull View view) {
FrameLayout playerContainer = view.findViewById(R.id.player_container);
View v = playerContainer.getChildAt(0);
if (v != null && v == mVideoView && !mVideoView.isFullScreen()) {
releaseVideoView();
}
}
});
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {// 过程:下拉刷新时,触发该方法
// refreshlayout.finishRefresh(2000/*,false*/);//传入false表示刷新失败
pageNum = 1; // 每次刷新 页数重置为1,就是回到首页
getVideoList(true); // 传入数据
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
// refreshlayout.finishLoadMore(2000/*,false*/);//传入false表示加载失败
pageNum ++; // 每加载一次,下一页
getVideoList(false);// 更新传入的数据
}
});
getVideoList(true);// 分页-2 第一次进来刷新
}
AndroidManifest.xml
<activity
android:name=".activity.HomeActivity"
android:exported="true"
android:configChanges="orientation|screenSize"/>
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import com.flyco.tablayout.SlidingTabLayout;
import com.ttit.myapp.R;
import com.ttit.myapp.adapter.HomeAdapter;
import java.util.ArrayList;
public class HomeFragment extends BaseFragment {
private ArrayList<Fragment> mFragments = new ArrayList<>();
private final String[] mTitles = {
"热门", "ios", "我的",
"我的", "我的", "我的", "我的"
};
private ViewPager viewPager;
private SlidingTabLayout slidingTabLayout;
public static HomeFragment newInstance (){
HomeFragment fragment = new HomeFragment();
return fragment;
}
@Override
protected int initLayout() {
return R.layout.fragment_home;
}
@Override
protected void initView() {
viewPager = mRootView.findViewById(R.id.fixedViewPager);
slidingTabLayout = mRootView.findViewById(R.id.slidingTabLayout);
}
@Override
protected void initData() {
//根据导航栏标题数量创建frag,传入对应的导航栏标题
for (String title : mTitles) {
mFragments.add(VideoFragment.newInstance(title));
}
//解决闪退问题-预加载
viewPager.setOffscreenPageLimit(mFragments.size());
//2 为viewpager设置适配器,该适配器包含frag
viewPager.setAdapter(new HomeAdapter(getFragmentManager(), mTitles, mFragments));
//3 绑定导航栏和viewpager,实现滑动效果
slidingTabLayout.setViewPager(viewPager);
}
}
package com.ttit.myapp.entity;
import java.io.Serializable;
import java.util.List;
/**
* @author: wuzhideren
* @date: 2022
**/
public class VideoCategoryResponse implements Serializable {
/**
* msg : success
* code : 0
* page : {"totalCount":8,"pageSize":10,"totalPage":1,"currPage":1,"list":[{"categoryId":1,"categoryName":"游戏"},{"categoryId":2,"categoryName":"音乐"},{"categoryId":3,"categoryName":"美食"},{"categoryId":4,"categoryName":"农人"},{"categoryId":5,"categoryName":"vlog"},{"categoryId":6,"categoryName":"搞笑"},{"categoryId":7,"categoryName":"宠物"},{"categoryId":8,"categoryName":"军事"}]}
*/
private String msg;
private int code;
private PageBean page;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public PageBean getPage() {
return page;
}
public void setPage(PageBean page) {
this.page = page;
}
public static class PageBean {
/**
* totalCount : 8
* pageSize : 10
* totalPage : 1
* currPage : 1
* list : [{"categoryId":1,"categoryName":"游戏"},{"categoryId":2,"categoryName":"音乐"},{"categoryId":3,"categoryName":"美食"},{"categoryId":4,"categoryName":"农人"},{"categoryId":5,"categoryName":"vlog"},{"categoryId":6,"categoryName":"搞笑"},{"categoryId":7,"categoryName":"宠物"},{"categoryId":8,"categoryName":"军事"}]
*/
private int totalCount;
private int pageSize;
private int totalPage;
private int currPage;
private List<CategoryEntity> list;
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getCurrPage() {
return currPage;
}
public void setCurrPage(int currPage) {
this.currPage = currPage;
}
public List<CategoryEntity> getList() {
return list;
}
public void setList(List<CategoryEntity> list) {
this.list = list;
}
}
}
package com.ttit.myapp.entity;
import java.io.Serializable;
/**
* @author: wuzhideren
* @date: 2022
**/
public class CategoryEntity implements Serializable {
/**
* categoryId : 1
* categoryName : 游戏
*/
private int categoryId;
private String categoryName;
public int getCategoryId() {
return categoryId;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
}
1 下载 https://www.mongodb.com/try/download/community
2 创建文件夹mongodb,放入解压内容
3 在其内,创建文件夹:data、logs
4 在根目录路径命令行,输入
mongod --install --dbpath D:\300IT\mongodb\data --logpath D:\300IT\mongodb\logs\mongodb.log
5 回车
若无报错,则成功
成功:
6 启动服务
net start mongodb
7 登录(验证是否安装成功)
mongo
易错 不是mongod
8 创建数据库且插入数据(复制粘贴办法在网上搜索)
db.video.insertMany([{
"vid" : NumberInt(1),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(2),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(3),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(4),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(5),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(6),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(7),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(8),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(9),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(10),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(11),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(12),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(13),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(14),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(15),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(16),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(17),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(18),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(19),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(20),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(21),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(22),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(23),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(24),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(25),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
},
{
"vid" : NumberInt(26),
"commentnum" : NumberInt(0),
"likenum" : NumberInt(0),
"collectnum" : NumberInt(0),
"flagLike" : false,
"flagCollect" : false
}])
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import com.flyco.tablayout.SlidingTabLayout;
import com.google.gson.Gson;
import com.ttit.myapp.R;
import com.ttit.myapp.activity.LoginActivity;
import com.ttit.myapp.adapter.HomeAdapter;
import com.ttit.myapp.api.Api;
import com.ttit.myapp.api.ApiConfig;
import com.ttit.myapp.api.TtitCallback;
import com.ttit.myapp.entity.CategoryEntity;
import com.ttit.myapp.entity.VideoCategoryResponse;
import com.ttit.myapp.entity.VideoEntity;
import com.ttit.myapp.entity.VideoListResponse;
import com.ttit.myapp.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class HomeFragment extends BaseFragment {
private ArrayList<Fragment> mFragments = new ArrayList<>();
private String[] mTitles;
private ViewPager viewPager;
private SlidingTabLayout slidingTabLayout;
public static HomeFragment newInstance (){
HomeFragment fragment = new HomeFragment();
return fragment;
}
@Override
protected int initLayout() {
return R.layout.fragment_home;
}
@Override
protected void initView() {
viewPager = mRootView.findViewById(R.id.fixedViewPager);
slidingTabLayout = mRootView.findViewById(R.id.slidingTabLayout);
}
@Override
protected void initData() {
getVideoCategoryList();
}
//获取视频分类数据
private void getVideoCategoryList() {//@isRefresh 分页-1 区分刷新还是下拉加载
String token = getStringFromSp("token");
//如果登录账户不为空
if (!StringUtils.isEmpty(token)){
HashMap<String, Object> params = new HashMap<>();//注意:是String,这是请求数据的容器
params.put("token", token);
Api.config(ApiConfig.VIDEO_CATEGORY_LIST, params).getRequest(new TtitCallback(){// 1 请求地址 2 请求参数
@Override
public void onSuccess(String res) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
VideoCategoryResponse response = new Gson().fromJson(res, VideoCategoryResponse.class);
if (response != null && response.getCode() == 0) { // 如果返回数据正常
List<CategoryEntity> list = response.getPage().getList(); // 获取格式化后返回数据
if (list != null && list.size() > 0){ // 如果有数据
mTitles = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
mTitles[i] = list.get(i).getCategoryName();
mFragments.add(VideoFragment.newInstance(list.get(i).getCategoryId()));
}
//解决闪退问题-预加载
viewPager.setOffscreenPageLimit(mFragments.size());
//2 为viewpager设置适配器,该适配器包含frag
viewPager.setAdapter(new HomeAdapter(getFragmentManager(), mTitles, mFragments));
//3 绑定导航栏和viewpager,实现滑动效果
slidingTabLayout.setViewPager(viewPager);
}
}
}
});
}
@Override
public void onFailure(Exception e) {
}
});
}else {//说明没有登录
navigateTo(LoginActivity.class);
}
}
}
/**
* @author wuzhideren
*/
package com.ttit.myapp.fragment;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
//import android.widget.VideoView; // 注意:别错包!!!
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.dueeeke.videocontroller.StandardVideoController;
import com.dueeeke.videocontroller.component.CompleteView;
import com.dueeeke.videocontroller.component.ErrorView;
import com.dueeeke.videocontroller.component.GestureView;
import com.dueeeke.videocontroller.component.TitleView;
import com.dueeeke.videocontroller.component.VodControlView;
import com.dueeeke.videoplayer.player.VideoView;
import com.google.gson.Gson;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;
import com.ttit.myapp.R;
import com.ttit.myapp.activity.LoginActivity;
import com.ttit.myapp.adapter.VideoAdapter;
import com.ttit.myapp.api.Api;
import com.ttit.myapp.api.ApiConfig;
import com.ttit.myapp.api.TtitCallback;
import com.ttit.myapp.entity.VideoEntity;
import com.ttit.myapp.entity.VideoListResponse;
import com.ttit.myapp.listener.OnItemChildClickListener;
import com.ttit.myapp.listener.OnItemClickListener;
import com.ttit.myapp.util.StringUtils;
import com.ttit.myapp.util.Tag;
import com.ttit.myapp.util.Utils;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
public class VideoFragment extends BaseFragment implements OnItemChildClickListener {
private int categoryId;//导航栏文字
private RecyclerView recyclerView;
private RefreshLayout refreshLayout;
private int pageNum = 1; // 页数
List<VideoEntity> datas = new ArrayList<>(); // 传入的数据
VideoAdapter videoAdapter;
protected VideoView mVideoView;
protected StandardVideoController mController;
protected ErrorView mErrorView;
protected CompleteView mCompleteView;
protected TitleView mTitleView;
LinearLayoutManager linearLayoutManager;
/**
* 当前播放的位置
*/
protected int mCurPos = -1;
/**
* 上次播放的位置,用于页面切回来之后恢复播放
*/
protected int mLastPos = mCurPos;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
videoAdapter.setDatas(datas);
videoAdapter.notifyDataSetChanged();
break;
}
}
};
public static VideoFragment newInstance (int categoryId){
VideoFragment fragment = new VideoFragment();
//接收传入的title
fragment.categoryId = categoryId;
return fragment;
}
@Override
protected int initLayout() {
return R.layout.fragment_video;
}
@Override
protected void initView() {
initVideoView();// 找到视频视图
recyclerView = mRootView.findViewById(R.id.recyclerView);
refreshLayout = mRootView.findViewById(R.id.refreshLayout);
}
// 过程与onViewCreated一样
@Override
protected void initData() {
linearLayoutManager = new LinearLayoutManager(getActivity());// 获取父活动对象
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);// 设置为垂直排列
recyclerView.setLayoutManager(linearLayoutManager);// 为视图对象,设置布局管理器
// 创建设配器
videoAdapter = new VideoAdapter(getActivity());
videoAdapter.setOnItemChildClickListener(this);
// 添加布局到 主页列表控件
recyclerView.setAdapter(videoAdapter);
recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
}
@Override
public void onChildViewDetachedFromWindow(@NonNull View view) {
FrameLayout playerContainer = view.findViewById(R.id.player_container);
View v = playerContainer.getChildAt(0);
if (v != null && v == mVideoView && !mVideoView.isFullScreen()) {
releaseVideoView();
}
}
});
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshlayout) {// 过程:下拉刷新时,触发该方法
// refreshlayout.finishRefresh(2000/*,false*/);//传入false表示刷新失败
pageNum = 1; // 每次刷新 页数重置为1,就是回到首页
getVideoList(true); // 传入数据
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshlayout) {
// refreshlayout.finishLoadMore(2000/*,false*/);//传入false表示加载失败
pageNum ++; // 每加载一次,下一页
getVideoList(false);// 更新传入的数据
}
});
getVideoList(true);// 分页-2 第一次进来刷新
}
// 视频视图信息配置
protected void initVideoView() {
mVideoView = new VideoView(getActivity());
mVideoView.setOnStateChangeListener(new com.dueeeke.videoplayer.player.VideoView.SimpleOnStateChangeListener() {
@Override
public void onPlayStateChanged(int playState) {
//监听VideoViewManager释放,重置状态
if (playState == com.dueeeke.videoplayer.player.VideoView.STATE_IDLE) {
Utils.removeViewFormParent(mVideoView);
mLastPos = mCurPos;
mCurPos = -1;
}
}
});
mController = new StandardVideoController(getActivity());
mErrorView = new ErrorView(getActivity());
mController.addControlComponent(mErrorView);
mCompleteView = new CompleteView(getActivity());
mController.addControlComponent(mCompleteView);
mTitleView = new TitleView(getActivity());
mController.addControlComponent(mTitleView);
mController.addControlComponent(new VodControlView(getActivity()));
mController.addControlComponent(new GestureView(getActivity()));
mController.setEnableOrientation(true);
mVideoView.setVideoController(mController);
}
@Override
public void onPause() {
super.onPause();
pause();
}
/**
* 由于onPause必须调用super。故增加此方法,
* 子类将会重写此方法,改变onPause的逻辑
*/
protected void pause() {
releaseVideoView();
}
@Override
public void onResume() {
super.onResume();
resume();
}
/**
* 由于onResume必须调用super。故增加此方法,
* 子类将会重写此方法,改变onResume的逻辑
*/
protected void resume() {
if (mLastPos == -1)
return;
//恢复上次播放的位置
startPlay(mLastPos);
}
/**
* PrepareView被点击
*/
@Override
public void onItemChildClick(int position) {
startPlay(position);
}
/**
* 开始播放
*
* @param position 列表位置
*/
protected void startPlay(int position) {
if (mCurPos == position) return;
if (mCurPos != -1) {
releaseVideoView();
}
VideoEntity videoEntity = datas.get(position);
//边播边存
// String proxyUrl = ProxyVideoCacheManager.getProxy(getActivity()).getProxyUrl(videoBean.getUrl());
// mVideoView.setUrl(proxyUrl);
mVideoView.setUrl(videoEntity.getPlayurl());
mTitleView.setTitle(videoEntity.getVtitle());
View itemView = linearLayoutManager.findViewByPosition(position);
if (itemView == null) return;
VideoAdapter.ViewHolder viewHolder = (VideoAdapter.ViewHolder) itemView.getTag();
//把列表中预置的PrepareView添加到控制器中,注意isPrivate此处只能为true。
mController.addControlComponent(viewHolder.mPrepareView, true);;//
Utils.removeViewFormParent(mVideoView);
viewHolder.mPlayerContainer.addView(mVideoView, 0);
//播放之前将VideoView添加到VideoViewManager以便在别的页面也能操作它
getVideoViewManager().add(mVideoView, Tag.LIST);
mVideoView.start();
mCurPos = position;
}
private void releaseVideoView() {
mVideoView.release();
if (mVideoView.isFullScreen()) {
mVideoView.stopFullScreen();
}
if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
mCurPos = -1;
}
//获取视频列表数据
private void getVideoList(boolean isRefresh) {//@isRefresh 分页-1 区分刷新还是下拉加载
String token = getStringFromSp("token");
//如果登录账户不为空
if (!StringUtils.isEmpty(token)){
HashMap<String, Object> params = new HashMap<>();//注意:是String,这是请求数据的容器
params.put("token", token);
params.put("page", pageNum);// 总页数
params.put("limit", ApiConfig.PAGE_SIZE);// 每页数量
params.put("categoryId", categoryId);
Api.config(ApiConfig.VIDEO_LIST_BY_CATEGORY, params).getRequest(new TtitCallback(){
@Override
public void onSuccess(String res) {
if (isRefresh){// 请求成功就关闭加载动画
refreshLayout.finishRefresh(true);
}else {
refreshLayout.finishLoadMore(true);// 注意!别和上面写成一样了
}
VideoListResponse response = new Gson().fromJson(res, VideoListResponse.class);
if (response != null && response.getCode() == 0) { // 如果返回数据正常
List<VideoEntity> list = response.getPage().getList(); // 获取格式化后返回数据
if (list != null && list.size() > 0){ // 如果有数据
if (isRefresh){// 如果时刷新
datas = list;
} else { // 如果加载
datas.addAll(list); // 追加加载后的数据
}
mHandler.sendEmptyMessage(0);
}else {
if (isRefresh){
// showToastSync();// 注意!不能使用
showToastSync("暂时无数据");
}else {
showToastSync("没有更多数据");
}
}
}
}
@Override
public void onFailure(Exception e) {
if (isRefresh){
refreshLayout.finishRefresh(true);
}else {
refreshLayout.finishLoadMore(true);
}
}
});
}else {//说明没有登录
navigateTo(LoginActivity.class);
}
}
}
优化代码
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
videoAdapter.setDatas(datas);
videoAdapter.notifyDataSetChanged();
break;
}
}
};
//....此处省略
@Override
public void onSuccess(String res) {
if (isRefresh){// 请求成功就关闭加载动画
refreshLayout.finishRefresh(true);
}else {
refreshLayout.finishLoadMore(true);// 注意!别和上面写成一样了
}
VideoListResponse response = new Gson().fromJson(res, VideoListResponse.class);
if (response != null && response.getCode() == 0) { // 如果返回数据正常
List<VideoEntity> list = response.getPage().getList(); // 获取格式化后返回数据
if (list != null && list.size() > 0){ // 如果有数据
if (isRefresh){// 如果时刷新
datas = list;
} else { // 如果加载
datas.addAll(list); // 追加加载后的数据
}
mHandler.sendEmptyMessage(0);
}else {
if (isRefresh){
// showToastSync();// 注意!不能使用
showToastSync("暂时无数据");
}else {
showToastSync("没有更多数据");
}
}
}
}
如果要转载,务必同时把参考资料源处转载
视频地址