仿DeepSeekAI 对话项目

#使用java开发的原生android简易聊天AI对话项目  大模型api使用硅基流动的。文章中的示例图片及视频全部由下面的代码生成。

#示例视频

原生android 仿deepseek 对话的简易聊天项目

以下是一些运行时的主要代码逻辑,非常的简易,适合刚学习android的同学参照学习

#主界面UI代码 RelativeLayout 相对布局不用处理,如使用约束布局可能需要对窗口进行键盘抬起的监听,由于是个学习的demo,我的示例中使用的RelativeLayout布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".MainActivity">
    <RelativeLayout
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentTop="true"
        >
        <TextView
            android:id="@+id/main_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="@color/white"
            android:gravity="center"
            android:paddingStart="80dp"
            android:paddingEnd="80dp"
            android:singleLine="true"
            android:text="@string/main_title"
            android:textSize="16sp"
            android:textStyle="bold" />
        <ImageView
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentEnd="true"
            android:layout_centerInParent="true"
            android:paddingEnd="15dp"
            android:src="@mipmap/top_is" />
    </RelativeLayout>
    <LinearLayout
        android:id="@+id/deepseek_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:orientation="vertical"
        android:paddingStart="50dp"
        android:paddingEnd="50dp">
        <ImageView
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_gravity="center"
            android:src="@mipmap/deepseek" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/deepseek"
            android:textSize="24sp"
            android:textStyle="bold" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@string/seekdesc"
            android:textColor="@color/textdefault"
            android:textSize="16sp"

            />
    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/chat_list_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_above="@+id/bottom"
        android:layout_below="@+id/toolbar"
        tools:listitem="@layout/message_item_layout"
        android:paddingBottom="0dp"
        android:clipToPadding="false"
        />

    <LinearLayout
        android:id="@+id/bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="20dp"
        android:orientation="vertical">

        <EditText
            android:id="@+id/message_text"
            style="@style/imExitStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="给deepseek发送消息"
            android:textColorHint="@color/textdefault"
            android:textSize="15sp" />

        <LinearLayout
            android:id="@+id/bottom_fn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"

            android:orientation="horizontal">

            <LinearLayout
                style="@style/bottom_fn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <ImageView
                    android:layout_width="25dp"
                    android:layout_height="25dp"
                    android:layout_gravity="center"
                    android:src="@mipmap/sd" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginLeft="5dp"
                    android:text="深度思考(V3)" />
            </LinearLayout>

            <LinearLayout
                style="@style/bottom_fn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="0dp">

                <ImageView
                    android:layout_width="25dp"
                    android:layout_height="25dp"
                    android:layout_gravity="center"
                    android:src="@mipmap/online_off" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginLeft="6dp"
                    android:text="联网搜索" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="right"
                android:paddingEnd="20dp">

                <ImageView
                    android:id="@+id/add"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_gravity="center"
                    android:layout_marginEnd="8dp"
                    android:src="@mipmap/add" />

                <ImageView
                    android:id="@+id/send"
                    android:layout_width="35dp"
                    android:layout_height="35dp"
                    android:layout_gravity="center"
                    android:src="@mipmap/send" />
            </LinearLayout>

        </LinearLayout>
    </LinearLayout>

</RelativeLayout>

#主界面java 逻辑代码

package com.example.deepseek;

import android.annotation.SuppressLint;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.deepseek.adapter.ChatAdapter;
import com.example.deepseek.entity.Answer;
import com.example.deepseek.entity.ChatEntity;
import com.example.deepseek.entity.Message;
import com.example.deepseek.entity.Question;
import com.example.deepseek.entity.StreamAnswer;
import com.example.deepseek.util.OkHttpUtils;
import com.google.gson.Gson;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText editText;
    private ImageView imageView;
    private LinearLayout bottomLayout;
    private LinearLayout linearLayout;
    private List<ChatEntity> chatEntities = new ArrayList<>();
    private RecyclerView recyclerView;
    private TextView textView;
    private ChatAdapter chatAdapter;
    private String baseUrl = "your api"; //我使用的是硅基流动的
    private String TAG = "main";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = findViewById(R.id.message_text);
        imageView = findViewById(R.id.send);
        recyclerView = findViewById(R.id.chat_list_view);
        linearLayout = findViewById(R.id.deepseek_title);
        textView = findViewById(R.id.main_title);
        imageView.setImageAlpha(122);
        imageView.setOnClickListener(this);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);

        editText.requestFocus();
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if (count > 0) {
                    imageView.setImageAlpha(255);
                } else {
                    imageView.setImageAlpha(122);
                }
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.send) {
            String content = editText.getText().toString();
            linearLayout.setVisibility(View.GONE);
            ChatEntity chatEntity_send = new ChatEntity(content, 0);
            ChatEntity chatEntity_rec = new ChatEntity("", 1);
            chatEntities.add(chatEntity_send);
            chatEntities.add(chatEntity_rec);
            textView.setText(chatEntities.get(0).getContent());
            chatAdapter = new ChatAdapter(chatEntities);
            recyclerView.setAdapter(chatAdapter);
            recyclerView.smoothScrollToPosition(chatAdapter.getItemCount() - 1);
            editText.getText().clear();
            Question question = new Question(content);
            String json = new Gson().toJson(question);
            Log.d(TAG, "onClick: " + json);
            receiveMessage(json);
        }
    }

    public void receiveMessage(String question) {

        OkHttpUtils.getInstance().doPost(baseUrl, question, new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                Log.d(TAG, "onFailure: " + e.getMessage());
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            InputStream inputStream = response.body().byteStream();
                            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                            String line;
                            String contentStr = "";
                            while ( (line = reader.readLine()) != null ) {
                                if(!line.isEmpty() && !line.equals("data: [DONE]")) {
                                    String newLine = "{" + line +"}";
                                    StreamAnswer answer = new Gson().fromJson(newLine, StreamAnswer.class);
                                    String message = answer.getAnswer().getChoices().get(0).getDelta().getContent();
                                    contentStr+=message;
                                    ChatEntity chatEntity = chatEntities.get(chatEntities.size() - 1);
                                    chatEntity.setContent(contentStr);
                                    chatEntities.set(chatEntities.size() - 1, chatEntity);
                                    chatAdapter.notifyItemChanged(chatEntities.size() - 1);
                                }
                            }
                            RecyclerView.LayoutManager layoutManager1 = recyclerView.getLayoutManager();
                            if(layoutManager1 !=null) {
                                int lastVisibleItemPosition = ((LinearLayoutManager) layoutManager1).findLastVisibleItemPosition();
                                if (lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition < recyclerView.getAdapter().getItemCount()) {
                                    // 获取对应的 ViewHolder
                                    RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(lastVisibleItemPosition);

                                    if (viewHolder != null) {
                                        // 获取子项的 View
                                        View lastItemView = viewHolder.itemView;
                                        // 获取高度
                                        int lastItemHeight = lastItemView.getHeight();
                                        recyclerView.setClipToPadding(false);
                                        recyclerView.setPadding(recyclerView.getPaddingLeft(),recyclerView.getPaddingTop(),recyclerView.getPaddingRight(),lastItemHeight +20);
                                    }
                                }

                            }
                            contentStr = "";
                        } catch (Exception e) {
                            ChatEntity chatEntity = chatEntities.get(chatEntities.size() - 1);
                            chatEntity.setContent("服务器异常,请稍后重试!");
                            chatEntities.set(chatEntities.size() - 1, chatEntity);
                            chatAdapter.notifyItemChanged(chatEntities.size() - 1);
                            Log.d(TAG, "run: " + e.getMessage());
                        }

                    }
                });

            }
        });


    }


}

#数据类

package com.example.deepseek.entity;

/**
 * 聊天类实体
 */
public class ChatEntity {
    //发送类型
    public static final int SEND = 0;
    //接收类型
    public static final int RECEIVE = 1;

    private String content;

    //收发类型
    private int type;

    public ChatEntity( String content, int type) {

        this.content = content;
        this.type = type;
    }

    public String getContent() {
        return content;
    }

    public int getType() {
        return type;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public void setType(int type) {
        this.type = type;
    }
}

#数据类2

package com.example.deepseek.entity;

import java.util.List;

public class Answer {
    private String id;
    private String model;
    private List<Choice> choices;
    public void setId(String id) {
        this.id = id;
    }
    public String getId() {
        return id;
    }
    public void setModel(String model) {
        this.model = model;
    }
    public String getModel() {
        return model;
    }

    public List<Choice> getChoices() {
        return choices;
    }

    public void setChoices(List<Choice> choices) {
        this.choices = choices;
    }
}

#数据类3

package com.example.deepseek.entity;

public class Message {
    private String role = "user";
    private String content;

    public Message( String content) {
        this.content = content;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

#数据类4

package com.example.deepseek.entity;

import com.example.deepseek.util.OkHttpUtils;

import java.util.ArrayList;
import java.util.List;

public class Question {
    private String model = "Qwen/Qwen2.5-72B-Instruct";
    private List<Message> messages;
    private Boolean stream = true;


    public Question(String message) {
        messages = new ArrayList<>();
        Message messageArr = new Message(message);
        if(messages.size()>0) {
            messages.set(0,messageArr);
        }else {
            messages.add(messageArr);
        }
    }

    public Boolean getStream() {
        return stream;
    }

    public void setStream(Boolean stream) {
        this.stream = stream;
    }

    public List<Message> getMessages() {
        return messages;
    }

    public void setMessages(List<Message> messages) {
        this.messages = messages;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

}

#数据类5

package com.example.deepseek.entity;

public class Choice {
    private Integer index;
    private StreamMessage delta;

    public Integer getIndex() {
        return index;
    }

    public void setIndex(Integer index) {
        this.index = index;
    }

    public StreamMessage getDelta() {
        return delta;
    }

    public void setDelta(StreamMessage delta) {
        this.delta = delta;
    }
}

#聊天的适配器代码

package com.example.deepseek.adapter;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.example.deepseek.R;
import com.example.deepseek.entity.ChatEntity;

import java.util.ArrayList;
import java.util.List;

public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ViewHolder> {

    List<ChatEntity> chatEntityList =new ArrayList<>();

    public ChatAdapter(List<ChatEntity> chatEntityList) {
        this.chatEntityList = chatEntityList;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View inflate = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_item_layout,parent,false);
        return new ViewHolder(inflate);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        ChatEntity chatEntity = chatEntityList.get(position);
        if (chatEntity.getType() == chatEntity.SEND) {
            holder.leftLayout.setVisibility(View.GONE);
            holder.rightLayout.setVisibility(View.VISIBLE);
            holder.rightContentTextView.setText(chatEntity.getContent());
        } else {
            holder.rightLayout.setVisibility(View.GONE);
            holder.leftLayout.setVisibility(View.VISIBLE);
            if(chatEntity.getContent().isEmpty()) {
                holder.progressBar.setVisibility(View.VISIBLE);
            }else {
                holder.progressBar.setVisibility(View.GONE);
                holder.leftContentTextView.setText(chatEntity.getContent());
            }
        }
    }

    @Override
    public int getItemCount() {
        return chatEntityList.size();
    }
    static class ViewHolder extends RecyclerView.ViewHolder  {
        TextView leftContentTextView;
        LinearLayout leftLayout;
        TextView rightContentTextView;
        LinearLayout rightLayout;
        ProgressBar progressBar;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            leftContentTextView = (TextView) itemView.findViewById(R.id.left_content);
            leftLayout = (LinearLayout) itemView.findViewById(R.id.left_bubble);
            rightContentTextView = (TextView) itemView.findViewById(R.id.right_content);
            rightLayout = (LinearLayout) itemView.findViewById(R.id.right_bubble);
            progressBar = itemView.findViewById(R.id.progressBar);
        }
    }
}

#封装的okhttp 请求代码

package com.example.deepseek.util;

import android.util.Log;

import org.json.JSONArray;

import java.io.IOException;
import java.util.Map;

import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;

public class OkHttpUtils {
    /**
     * 单例模式
     */
    private static OkHttpUtils okHttpUtils = null;

    private OkHttpUtils() {
    }

    public static OkHttpUtils getInstance() {
        //双层判断,同步锁
        if (okHttpUtils == null) {
            synchronized (OkHttpUtils.class) {
                if(okHttpUtils == null){
                    okHttpUtils = new OkHttpUtils();
                }
            }
        }
        return okHttpUtils;
    }

    /**
     * 单例模式
     * 封装OkhHttp
     * synchronized同步方法
     */
    private static OkHttpClient okHttpClient = null;

    private static synchronized OkHttpClient getOkHttpClient() {
        if (okHttpClient == null) {
            //拦截器
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(String message) {
                    //拦截日志消息
                    Log.i("lj", "log: " + message);
                }
            });
            //设置日志拦截器模式
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

            okHttpClient = new OkHttpClient.Builder()
                    //日志拦截器
                    .addInterceptor(interceptor)
                    //应用拦截器
                    .addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Chain chain) throws IOException {
                            Request request = chain.request()
                                    .newBuilder()
                                    .addHeader("source", "android")
                                    .build();

                            return chain.proceed(request);
                        }
                    })
                    .build();
        }
        return okHttpClient;
    }

    /**
     * doGet
     */
    public void doGet(String url, Callback callback) {
        //创建okhttp
        OkHttpClient okHttpClient = getOkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .build();
        okHttpClient.newCall(request).enqueue(callback);
    }

    /**
     * doPost
     */
    public void doPost(String url, Map<String, String> params, Callback callback) {
        OkHttpClient okHttpClient = getOkHttpClient();
        //请求体
        FormBody.Builder formBody = new FormBody.Builder();
        for (String key : params.keySet()) {
            //遍历map集合
            formBody.add(key, params.get(key));
        }
        Request request = new Request.Builder()
                .url(url)
                .post(formBody.build())
                .addHeader("Authorization","Bearer sk-gvyrsdjkleevdmyqwhdhljpbaeyzmtxrjbeqxbrotzgwrjpw")
                .addHeader("Content-Type", "application/json")
                .build();
        okHttpClient.newCall(request).enqueue(callback);
    }
    public void doPost(String url, String jsonBody, Callback callback) {
        OkHttpClient okHttpClient = getOkHttpClient();
        RequestBody requestBody = RequestBody.create(jsonBody, MediaType.get("application/json; charset=utf-8"));
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("Authorization","Bearer sk-gvyrsdjkleevdmyqwhdhljpbaeyzmtxrjbeqxbrotzgwrjpw")
                .addHeader("Content-Type", "application/json")
                .build();
        okHttpClient.newCall(request).enqueue(callback);
    }
}

#备注 刚学习 代码不够精简,主要是java 开发android 已经慢慢被废弃了,后面又重新学习kotlin 开始新的练习,不过仅供参考,希望对你的学习有所帮助

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值