文章目录
- 项目演示
- 一、搭建云服务器
- 我选择的服务器
- 搭建服务器环境
- 部署自己的项目
- 二、测试接口
- 我使用的是postman,其实也可以用浏览器直接F12,然后NetWork,懂的都懂。
- 三、android项目环境搭建
- 我遇到的问题
- Gradle配置
- SDK配置
- NDK配置
- Android项目实现
- app进入页面实现(welcomeActivity)
- 登录页面实现(LoginActivity)
- 注册页面实现(RegisterActivity)
- 主页面实现(MainActivity)
- slide页面实现
- home页面实现
- chat页面实现
- 总结
项目演示
演示actcBlog
一、搭建云服务器
我选择的服务器
据我了解云服务器主流是有阿里云的和腾讯云等平台提供的。我在此使用的是阿里云的云服务器ECS。
搭建服务器环境
这个搭建的教程网上都有,但是比较乱,我选择的是使用宝塔linux面板,如何在新开的云服务器里面安装宝塔面板,下图B站视频博主写的流程,在此引用一下:
这是链接云服务器搭建
部署自己的项目
在宝塔面板上打开网站(我使用的是原生的java项目tomcat部署)
添加java项目选择独立项目,域名就是你的ip,放行端口就是要使用的端口(这里的端口你还需要在阿里云云服务管理控制平台里面的安全组添加上允许)
项目的部署路径我建议使用我这个路径,这样直接放在对应的webapps文件夹下就好了,基本不会出错(不要使用提供的基础路径!!!)完成之后提交就好了,可以用浏览器直接访问你的项目了(http://ip:port/项目名/对应的接口啥的)
二、测试接口
我使用的是postman,其实也可以用浏览器直接F12,然后NetWork,懂的都懂。
这里举例一个测试成功的请求
怎么测试这个很简单,可以去找对应的相关视频学习,这里就不推荐了。
三、android项目环境搭建
我遇到的问题
Android studio在使用中会经常从网站上下载资源然后进行重定位,这样本意是挺好的,但是下载不快还经常出错…这个原因不解释,除了环境配置上面的问题,后面还有一些会遇到的搜不到的问题解决方案,如果需要,就看我后面项目实现的那些。
Gradle配置
我在新建项目后,这个gradle的JDK版本可能有些兼容问题,于是我修改了JDK版本,这个可以下载,奇怪的是,这个下载速度很快
更换JDK后,Gradle还有一项可以配置
建议使用国内的镜像
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-6.7.1-all.zip
还有就是下载资源连接超时这种问题,可以添加依赖
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.10")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10") // 确保与主库版本一致
SDK配置
如果出现SDK版本不适配的问题,建议删掉SDK本地文件重新下载,这个报错没法复现,但是在此提示一下。
NDK配置
这个可以不用管,这个没有需求不加就好了
Android项目实现
项目源码我会放在GitHub上面供学习,后期我会把链接放上,这里主要是介绍项目功能实现,
然后是遇到的一些问题解决方案,有兴趣可以看看。
app进入页面实现(welcomeActivity)
我使用banner做了一个轮播图,通过点击事件,可以进入到登录页面
添加的依赖
`implementation("io.github.youth5201314:banner:2.2.2")`
在下图里面添加,后面不在介绍这里,直接写依赖
登录页面实现(LoginActivity)
我使用了一个视频资源作为背景,登录是通过网络传参(GSON)对比认证
这里需要添加依赖,还有入网许可
先是依赖
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.squareup.okhttp3:okhttp:3.12.0")
implementation("com.squareup.okio:okio:1.15.0")
然后是许可
<!-- 访问网络权限-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 访问sd卡需要添加的权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
注册页面实现(RegisterActivity)
在登录页面跳转这里的时候我传递了用户名name,这里的注册只是在本地保存一份数据,并没有实现真正的注册
这里没有什么问题,继续下一个
主页面实现(MainActivity)
主页面在创建的时候我使用了框架Navigation Drawer Activity,我这里遇到很多问题,首先主页面演示
这里主页面的背景使用的也是视频,都是动态背景
slide页面实现
这个比较麻烦,直接放代码
package com.example.actcblog.ui.slideshow;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.example.actcblog.LoginActivity;
import com.example.actcblog.R;
import com.example.actcblog.RegisterActivity;
import com.example.actcblog.databinding.FragmentSlideshowBinding;
import com.example.actcblog.databinding.ItemTransformBinding;
import com.example.actcblog.ui.chat.ChatActivity;
import com.example.actcblog.ui.gallery.GalleryFragment;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class SlideshowFragment extends Fragment {
private FragmentSlideshowBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
SlideshowViewModel slideshowViewModel =new ViewModelProvider(this).get(SlideshowViewModel.class);
binding = FragmentSlideshowBinding.inflate(inflater, container, false);
View root = binding.getRoot();
RecyclerView recyclerView = binding.recyclerviewTransform;
ListAdapter<String, TransformViewHolder> adapter = new TransformAdapter();
recyclerView.setAdapter(adapter);
slideshowViewModel.getTexts().observe(getViewLifecycleOwner(), adapter::submitList);
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private class TransformAdapter extends ListAdapter<String, TransformViewHolder> {
private final List<Integer> drawables = Arrays.asList(
R.drawable.avatar_1,
R.drawable.avatar_2,
R.drawable.avatar_3,
R.drawable.avatar_4,
R.drawable.avatar_5,
R.drawable.avatar_6,
R.drawable.avatar_7,
R.drawable.avatar_8,
R.drawable.avatar_9,
R.drawable.avatar_10,
R.drawable.avatar_11,
R.drawable.avatar_12,
R.drawable.avatar_13,
R.drawable.avatar_14,
R.drawable.avatar_15,
R.drawable.avatar_16);
protected TransformAdapter() {
super(new DiffUtil.ItemCallback<String>() {
@Override
public boolean areItemsTheSame(@NonNull String oldItem, @NonNull String newItem) {
return oldItem.equals(newItem);
}
@Override
public boolean areContentsTheSame(@NonNull String oldItem, @NonNull String newItem) {
return oldItem.equals(newItem);
}
});
}
@NonNull
@Override
public TransformViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemTransformBinding binding = ItemTransformBinding.inflate(LayoutInflater.from(parent.getContext()));
return new TransformViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull TransformViewHolder holder, int position) {
/*
设置联系人每个长条的宽度
*/
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
holder.lineLayout.getLayoutParams().width = screenWidth;
holder.textView.setText(getItem(position));
holder.imageView.setImageDrawable(
ResourcesCompat.getDrawable(holder.imageView.getResources(),
drawables.get(position),
null));
holder.lineLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), ChatActivity.class);
intent.putExtra("name",holder.textView.getText().toString());
startActivity(intent);
}
});
}
}
public static class TransformViewHolder extends RecyclerView.ViewHolder {
private final ImageView imageView;
private final TextView textView;
private final LinearLayout lineLayout;
public TransformViewHolder(ItemTransformBinding binding) {
super(binding.getRoot());
lineLayout = binding.lineLayout;
imageView = binding.imageViewItemTransform;
textView = binding.textViewItemTransform;
}
}
}
package com.example.actcblog.ui.slideshow;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.ArrayList;
import java.util.List;
public class SlideshowViewModel extends ViewModel {
private final MutableLiveData<List<String>> mTexts;
public SlideshowViewModel() {
mTexts = new MutableLiveData<>();
ArrayList<String> names = new ArrayList<>();
names.add("xxx");
names.add("软件2班");
names.add("韩国炸鸡老板");
names.add("烤冷面老板");
names.add("柯南");
names.add("弗兰克");
names.add("灰原哀");
names.add("海贼王");
names.add("睡觉的小黑");
names.add("摘玫瑰的小新");
names.add("张三");
names.add("李四");
names.add("王五");
names.add("测试");
names.add("好烦");
names.add("必须高分!");
List<String> texts = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
texts.add(names.get(i));
}
mTexts.setValue(texts);
}
public LiveData<List<String>> getTexts() {
return mTexts;
}
}
home页面实现
因为用的框架,这里是一个容器对应的事件添加放在了homefragment里面
package com.example.actcblog.ui.home;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.example.actcblog.databinding.FragmentHomeBinding;
public class HomeFragment extends Fragment {
private WebView webView;
private FragmentHomeBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
HomeViewModel homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView textView = binding.textHome;
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
initWebView();
return root;
}
private void initWebView() {
binding.webView.loadUrl("https://hunan.voc.com.cn/article/202312/202312281735037065.html");
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
文本的显示需要在一个实例里面给他中转一下
package com.example.actcblog.ui.home;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class HomeViewModel extends ViewModel {
private final MutableLiveData<String> mText;
public HomeViewModel() {
mText = new MutableLiveData<>();
mText.setValue("\t\t重点功能:\n" +
"1、联系人,点击左上角的按钮,可以看到我的导航菜单,点击联系人,进入下一个界面,选择联系人可以进行聊天\n\n" +
"2、新闻快达,点击那个小信箱可以查看新闻,在新闻界面再次点击小信箱则返回主界面\n\n");
}
public LiveData<String> getText() {
return mText;
}
}
chat页面实现
直接放代码,不多废话
package com.example.actcblog.ui.chat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.actcblog.MainActivity;
import com.example.actcblog.R;
import com.example.actcblog.ui.slideshow.SlideshowFragment;
public class ChatActivity extends Activity {
private RelativeLayout relativeLayout;
private Button BtnSend;
private TextView chat_name;
private EditText InputBox;
private List<ChatMessage> mData;
private ChatAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_chat);
final ListView mListView=(ListView)findViewById(R.id.MainList);
final RelativeLayout relativeLayout=(RelativeLayout)findViewById(R.id.relativeLayout2);
relativeLayout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(ChatActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
});
chat_name = findViewById(R.id.chat_name);
Intent intent = getIntent();
chat_name.setText(intent.getStringExtra("name"));
mData=LoadData();
mAdapter=new ChatAdapter(this, mData);
mListView.setAdapter(mAdapter);
mListView.smoothScrollToPositionFromTop(mData.size(), 0);
InputBox=(EditText)findViewById(R.id.InputBox);
BtnSend=(Button)findViewById(R.id.BtnSend);
BtnSend.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View view)
{
InputMethodManager imm=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
if(!InputBox.getText().toString().isEmpty())
{
//获取时间
Calendar c=Calendar.getInstance();
StringBuilder mBuilder=new StringBuilder();
mBuilder.append(Integer.toString(c.get(Calendar.YEAR))+"年");
mBuilder.append(Integer.toString(c.get(Calendar.MONTH))+"月");
mBuilder.append(Integer.toString(c.get(Calendar.DATE))+"日");
mBuilder.append(Integer.toString(c.get(Calendar.HOUR_OF_DAY))+":");
mBuilder.append(Integer.toString(c.get(Calendar.MINUTE)));
//构造时间消息
ChatMessage Message=new ChatMessage(ChatMessage.MessageType_Time,mBuilder.toString());
mData.add(Message);
//构造输入消息
Message=new ChatMessage(ChatMessage.MessageType_To,InputBox.getText().toString());
mData.add(Message);
//构造返回消息,如果这里加入网络的功能,那么这里将变成一个网络机器人
Message=new ChatMessage(ChatMessage.MessageType_From,InputBox.getText().toString()+"(这里是返回的可以修改)");
mData.add(Message);
//更新数据
mAdapter.Refresh();
}else
{
Toast.makeText(getApplicationContext(), "输入为空,请先输入内容", Toast.LENGTH_SHORT).show();
return;
}
//清空输入框
InputBox.setText("");
//关闭输入法
imm.hideSoftInputFromWindow(null, InputMethodManager.HIDE_IMPLICIT_ONLY);
//滚动列表到当前消息
mListView.smoothScrollToPositionFromTop(mData.size(), 0);
}
});
}
private List<ChatMessage> LoadData()
{
/*
预留数据进行测试
*/
List<ChatMessage> Messages=new ArrayList<ChatMessage>();
ChatMessage Message=new ChatMessage(ChatMessage.MessageType_Time,"2023年12月27日");
Messages.add(Message);
Message=new ChatMessage(ChatMessage.MessageType_From,"测试测试,能收到吗?");
Messages.add(Message);
Message=new ChatMessage(ChatMessage.MessageType_To,"欧克欧克");
Messages.add(Message);
Message=new ChatMessage(ChatMessage.MessageType_From,"OK!OK!!");
Messages.add(Message);
Message=new ChatMessage(ChatMessage.MessageType_To,"测试成功");
Messages.add(Message);
Message=new ChatMessage(ChatMessage.MessageType_Time,"1:08");
Messages.add(Message);
return Messages;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
return true;
}
}
package com.example.actcblog.ui.chat;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.actcblog.R;
public class ChatAdapter extends BaseAdapter
{
private Context mContext;
private List<ChatMessage> mData;
public ChatAdapter(Context context, List<ChatMessage> data)
{
this.mContext=context;
this.mData=data;
}
public void Refresh()
{
this.notifyDataSetChanged();
}
@Override
public int getCount()
{
return mData.size();
}
@Override
public Object getItem(int Index)
{
return mData.get(Index);
}
@Override
public long getItemId(int Index)
{
return Index;
}
@Override
public View getView(int Index, View mView, ViewGroup mParent)
{
TextView Content;
switch(mData.get(Index).getType())
{
case ChatMessage.MessageType_Time:
mView=LayoutInflater.from(mContext).inflate(R.layout.atimemessage, null);
Content=(TextView)mView.findViewById(R.id.Time);
Content.setText(mData.get(Index).getContent());
break;
case ChatMessage.MessageType_From:
mView=LayoutInflater.from(mContext).inflate(R.layout.agetmessage, null);
Content=(TextView)mView.findViewById(R.id.From_Content);
Content.setText(mData.get(Index).getContent());
break;
case ChatMessage.MessageType_To:
mView=LayoutInflater.from(mContext).inflate(R.layout.asendmessage, null);
Content=(TextView)mView.findViewById(R.id.To_Content);
Content.setText(mData.get(Index).getContent());
break;
}
return mView;
}
}
package com.example.actcblog.ui.chat;
public class ChatMessage
{
//定义3种布局类型
public static final int MessageType_Time=0;
public static final int MessageType_From=1;
public static final int MessageType_To=2;
public ChatMessage(int Type, String Content)
{
this.mType=Type;
this.mContent=Content;
}
//消息类型
private int mType;
//消息内容
private String mContent;
//获取类型
public int getType() {
return mType;
}
//设置类型
public void setType(int mType) {
this.mType = mType;
}
//获取内容
public String getContent() {
return mContent;
}
//设置内容
public void setContent(String mContent) {
this.mContent = mContent;
}
}
<LinearLayout 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:orientation="vertical"
tools:context=".ui.chat.ChatActivity" >
<RelativeLayout
android:id="@+id/relativeLayout2"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="#66DD00" >
<ImageView
android:id="@+id/imageView2"
android:layout_width="45dp"
android:layout_height="45dp"
android:background="@drawable/go_back_selector"
/>
<TextView
android:id="@+id/chat_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textColor="@color/black"
android:textSize="20sp" />
</RelativeLayout>
<ListView
android:id="@+id/MainList"
android:layout_width="match_parent"
android:layout_height="0dip"
android:divider="@color/white"
android:layout_weight="0.25" >
</ListView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="bottom" >
<EditText
android:id="@+id/InputBox"
android:layout_width="278dp"
android:layout_height="40dp"
android:layout_weight="1"
android:background="@drawable/shape_linear"
android:ems="10"
android:inputType="text"
android:textColor="@color/black">
<requestFocus />
</EditText>
<Button
android:id="@+id/BtnSend"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="1"
android:background="@drawable/selector_btn"
android:text="发送"
android:textColor="#ffffff"/>
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ImageView
android:id="@+id/Header"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="3dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="3dp"
android:layout_marginBottom="3dp"
android:contentDescription="聊天"
android:src="@drawable/avatar_1" />
<TextView
android:id="@+id/From_Content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_toRightOf="@+id/Header"
android:background="@drawable/shape_linear"
android:gravity="center"
android:textSize="15dp"
android:textIsSelectable="false" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="@+id/Time"
android:layout_width="150dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="3dp"
android:background="@null"
android:gravity="center"
android:textColor="@color/black"
android:textIsSelectable="false"
android:textSize="14sp" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ImageView
android:id="@+id/Header"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_margin="3dp"
android:src="@drawable/avatar_2" />
<TextView
android:id="@+id/To_Content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_toLeftOf="@+id/Header"
android:textIsSelectable="false"
android:background="@drawable/selector_btn"
android:textSize="15dp"
android:gravity="center" />
</RelativeLayout>
总结
后面主页面具体代码实现的功能上述都有覆盖,若是需要学习,可以进行研究,算是给出的一个大体方向,代码量不算多,但是都是自己的思考加进去的功能,学会思考很重要,再一个是兼容问题,这个问题相当严重,网络上好在都有帮助,在这段时间的学习下,我觉得有时候问题也可以换一种方式思路去回答,一个问题不止一个答案,多思考,合理使用工具,最后希望我这份期末设计得一个高分,这样我也能对学校课程的坚持感受到是有回报的。
源码后续会给,我会放在GitHub上面。如有需要,请联系。