从Android端到服务端全端开发------二级评论表的实现

前言:对于专门开发android端或者服务端某一端的开发者来说,对另一方可能也不太熟悉,希望通过这篇文章使大家更加熟悉另外一端,让开发协作变得更加默契。

web服务器端和app服务器端的区别:

几乎一样,不过作为app,以下几点是需要考虑的:

1、用户的手机流量,由于手机套餐流量是一定的,不可能让用户每次都打开原图浏览,要根据用户需要再决定原图还是压缩图,还有就是要根据手机的尺寸去裁剪图片的尺寸。

2、用户的手机电量,因为每一次的请求数据都会消耗不少的电量,所以尽量要减少应用的数据请求次数。

3、数据的丢失,因为用户手机上的信号是根据所处的环境变化而变化,当信号弱的时候,数据请求会有丢失的情况,加载不全。

用到的技术:

Android端:

app主体架构:ViewPager+Tablayout+Fragment,RecycleView嵌套RecycleView,ListView

网络请求:OkHttp

解析json:Gson

服务器端:

Spring MVC+Spring+Mybatis+Spring Data JPA+SpringBoot2.0

二级评论表的数据库设计可参照简书作者:杰哥长得帅

传送门:https://www.jianshu.com/p/5b757583eca7

模仿软件:最右APP(最右的忠实粉丝)

最终效果:(界面完全没优化,头像等个人信息也没显示出来,但是数据已经有了)

数据库设计

invitation表(帖子表)

comment表(评论表)

reply表(回复表)

user表(用户表)

服务器端: 

项目结构:

首先使用IntlliJ Idea的Spring Initializr器创建项目,并添加mybatis、spring data jpa和web相应的starter依赖

config:配置类,相当于配置文件xml

controller:控制层

entity和pojo:均为模型类,pojo作mybatis的模型,entity作jpa的模型

mapper和repository:均为dao层,mapper作mybatis的dao,repository作的jpa的dao

service:服务层

utils:工具类

application.properties:配置文件

pojo

InvitationCustomer
public class InvitationCustomer {
    //主键,id不要导错包
    private Integer id;
    //帖子内容
    private String invContent;
    //帖子赞数
    private Integer invLaud;
    //一个帖子有多个评论
    private List<CommentCustomer> comments;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getInvContent() {
        return invContent;
    }

    public void setInvContent(String invContent) {
        this.invContent = invContent;
    }

    public Integer getInvLaud() {
        return invLaud;
    }

    public void setInvLaud(Integer invLaud) {
        this.invLaud = invLaud;
    }

    public List<CommentCustomer> getComments() {
        return comments;
    }

    public void setComments(List<CommentCustomer> comments) {
        this.comments = comments;
    }

    @Override
    public String toString() {
        return "InvitationCustomer{" +
                "id=" + id +
                ", invContent='" + invContent + '\'' +
                ", invLaud=" + invLaud +
                ", comments=" + comments +
                '}';
    }
}
CommentCustomer
public class CommentCustomer {
    //主键
    private Integer id;
    //评论时间
    private Date comDate;
    //评论内容
    private String comContent;
    //评论赞数
    private Integer comLaud;
    //帖子的外键
    private Integer invId;
    //评论用户的外键
    private Integer userId;
    //评论用户
    private UserCustomer user;
    //一个评论有多个嵌套评论
    private List<ReplyCustomer> replys;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Date getComDate() {
        return comDate;
    }

    public void setComDate(Date comDate) {
        this.comDate = comDate;
    }

    public String getComContent() {
        return comContent;
    }

    public void setComContent(String comContent) {
        this.comContent = comContent;
    }

    public Integer getComLaud() {
        return comLaud;
    }

    public void setComLaud(Integer comLaud) {
        this.comLaud = comLaud;
    }

    public UserCustomer getUser() {
        return user;
    }

    public void setUser(UserCustomer user) {
        this.user = user;
    }

    public Integer getInvId() {
        return invId;
    }

    public void setInvId(Integer invId) {
        this.invId = invId;
    }

    public List<ReplyCustomer> getReplys() {
        return replys;
    }

    public void setReplys(List<ReplyCustomer> replys) {
        this.replys = replys;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    @Override
    public String toString() {
        return "CommentCustomer{" +
                "id=" + id +
                ", comDate=" + comDate +
                ", comContent='" + comContent + '\'' +
                ", comLaud=" + comLaud +
                ", invId=" + invId +
                ", userId=" + userId +
                ", user=" + user +
                ", replys=" + replys +
                '}';
    }
}
ReplyCustomer
public class ReplyCustomer {
    //主键
    private Integer id;
    //回复类型
    private Integer reType;
    //回复内容
    private String reContent;
    //回复的赞数
    private Integer reLaud;
    //回复的帖子外键
    private Integer comId;
    //回复人的外键
    private Integer userIdFrom;
    //被回复人的外键
    private Integer userIdTo;
    //回复人
    private UserCustomer userFrom;
    //被回复人
    private UserCustomer userTo;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getReType() {
        return reType;
    }

    public void setReType(Integer reType) {
        this.reType = reType;
    }

    public String getReContent() {
        return reContent;
    }

    public void setReContent(String reContent) {
        this.reContent = reContent;
    }

    public Integer getReLaud() {
        return reLaud;
    }

    public void setReLaud(Integer reLaud) {
        this.reLaud = reLaud;
    }

    public Integer getComId() {
        return comId;
    }

    public void setComId(Integer comId) {
        this.comId = comId;
    }

    public UserCustomer getUserFrom() {
        return userFrom;
    }

    public void setUserFrom(UserCustomer userFrom) {
        this.userFrom = userFrom;
    }

    public UserCustomer getUserTo() {
        return userTo;
    }

    public void setUserTo(UserCustomer userTo) {
        this.userTo = userTo;
    }

    public Integer getUserIdFrom() {
        return userIdFrom;
    }

    public void setUserIdFrom(Integer userIdFrom) {
        this.userIdFrom = userIdFrom;
    }

    public Integer getUserIdTo() {
        return userIdTo;
    }

    public void setUserIdTo(Integer userIdTo) {
        this.userIdTo = userIdTo;
    }

    @Override
    public String toString() {
        return "ReplyCustomer{" +
                "id=" + id +
                ", reType=" + reType +
                ", reContent='" + reContent + '\'' +
                ", reLaud=" + reLaud +
                ", comId=" + comId +
                ", userIdFrom=" + userIdFrom +
                ", userIdTo=" + userIdTo +
                ", userFrom=" + userFrom +
                ", userTo=" + userTo +
                '}';
    }
}
UserCustomer
public class UserCustomer {
    //主键id
    private Integer id;
    //登录名
    private String userLoginname;
    //登录密码
    private String userPassword;
    //姓名
    private String userName;
    //学号
    private String userStudentID;
    //头像
    private String userImage;
    //微信号
    private String userWxNumber;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserLoginname() {
        return userLoginname;
    }

    public void setUserLoginname(String userLoginname) {
        this.userLoginname = userLoginname;
    }

    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserStudentID() {
        return userStudentID;
    }

    public void setUserStudentID(String userStudentID) {
        this.userStudentID = userStudentID;
    }

    public String getUserImage() {
        return userImage;
    }

    public void setUserImage(String userImage) {
        this.userImage = userImage;
    }

    public String getUserWxNumber() {
        return userWxNumber;
    }

    public void setUserWxNumber(String userWxNumber) {
        this.userWxNumber = userWxNumber;
    }

    @Override
    public String toString() {
        return "UserCustomer{" +
                "id=" + id +
                ", userLoginname='" + userLoginname + '\'' +
                ", userPassword='" + userPassword + '\'' +
                ", userName='" + userName + '\'' +
                ", userStudentID='" + userStudentID + '\'' +
                ", userImage='" + userImage + '\'' +
                ", userWxNumber='" + userWxNumber + '\'' +
                '}';
    }
}

entity

Invitation
@Entity
@Table(name = "invitation")
public class Invitation {
    //主键,id不要导错包
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    //帖子内容
    @Column(name = "inv_content")
    private String invContent;
    //帖子赞数
    @Column(name = "inv_laud")
    private Integer invLaud;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getInvContent() {
        return invContent;
    }

    public void setInvContent(String invContent) {
        this.invContent = invContent;
    }

    public Integer getInvLaud() {
        return invLaud;
    }

    public void setInvLaud(Integer invLaud) {
        this.invLaud = invLaud;
    }
}

Controller层,@ResponseBody返回的json数据格式,是和客户端交互的最重要一环

InvitationController
@Controller
public class InvitationController {
    //依赖注入
    @Autowired
    private InvitationService invitationService;

    
    //找到所有的树洞
    @ResponseBody//返回json格式,此处做了一个分页的操作,startPage开始页数,sizePage页面数据
    @RequestMapping(value = "/invs/{startPage}/{sizePage}",method = RequestMethod.GET)
    public Page<Invitation> findAllInvitations(@PathVariable("startPage") int startPage,@PathVariable("sizePage") int sizePage){
        return invitationService.findAllInvitations(startPage,sizePage);
    }

    //某条树洞详情
    @ResponseBody
    @RequestMapping(value = "/inv/{id}",method = RequestMethod.GET)
    public List<CommentCustomer> findDetalInvitation(@PathVariable("id") int id){
        return invitationService.findDetalInvitation(id);
    }
}
InvitationService
public interface InvitationService {
    //找到所有的帖子,分页
    public Page<Invitation> findAllInvitations(int startPage, int sizePage);
    //进入帖子详情
    public List<CommentCustomer> findDetalInvitation(Integer id);
 }
InvitationServiceImpl
@Service
public class InvitationServiceImpl implements InvitationService{
    //注入两个dao层对象
    @Autowired
    private InvitationRepository invitationRepository;
    @Autowired
    private InvitationCustomerMapper invitationCustomerMapper;

    
    @Override
    public Page<Invitation> findAllInvitations(int startPage,int sizePage) {
        Sort sort=new Sort(Sort.Direction.DESC,"id");
        Pageable pageable=new PageRequest(startPage,sizePage,sort);
        Page<Invitation> page = invitationRepository.findAll(pageable);
        return page;
    }

    @Override
    public List<CommentCustomer> findDetalInvitation(Integer id) {
        return invitationCustomerMapper.findInvDetailById(id);
    }

}
InvitationRepository是采用jpa方式,实现接口就可以进行增删改查,适合于单表操作
public interface InvitationRepository extends JpaRepository<Invitation,Integer>,JpaSpecificationExecutor<Invitation>{
}
InvitationCustomerMapper是采用mybatis的方式,更适合于多表复杂的数据访问
public interface InvitationCustomerMapper {
    //根据帖子id查询所有评论以及每一条评论的用户
    public List<CommentCustomer> findInvDetailById(Integer id);
}

对应的InvitationCustomerMapper.xml文件

多表关联,存在一对多的关系,注意,千万不要使用内连接!!!

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zdxh.assistant.mapper.InvitationCustomerMapper">
       <select id="findInvDetailById" resultMap="findInvDetail" parameterType="integer">
        SELECT
          comment.id cid,
          comment.com_date,
          comment.com_content,
          comment.com_laud,
          comment.user_id,
          comment.inv_id,
          user.id uid,
          user.user_loginname,
          user.user_image,
		  reply.id rid,
          reply.re_type,
          reply.re_content,
          reply.re_laud,
          reply.com_id,
          reply.user_id_from,
          reply.user_id_to,
          user_from.id uid,
          user_from.user_loginname,
          user_from.user_image
        FROM invitation
        LEFT JOIN comment
        ON invitation.id=comment.inv_id
        LEFT JOIN user
        ON comment.user_id=user.id
        LEFT JOIN reply
		ON `comment`.id=reply.com_id
		LEFT JOIN user user_from
        ON reply.user_id_from=user_from.id
        WHERE invitation.id=#{id}
    </select>

    <resultMap id="findInvDetail" type="cn.zdxh.assistant.pojo.CommentCustomer">
        <id property="id" column="cid"/>
        <result property="comDate" column="com_date"/>
        <result property="comContent" column="com_content"/>
        <result property="comLaud" column="com_laud"/>
        <result property="userId" column="user_id"/>
        <result property="invId" column="inv_id"/>
        <association property="user" javaType="cn.zdxh.assistant.pojo.UserCustomer">
            <id property="id" column="uid"/>
            <result property="userLoginname" column="user_loginname"/>
            <result property="userImage" column="user_image"/>
        </association>
        <collection property="replys" ofType="cn.zdxh.assistant.pojo.ReplyCustomer">
            <id property="id" column="rid"/>
            <result property="reType" column="re_type"/>
            <result property="reContent" column="re_content"/>
            <result property="reLaud" column="re_laud"/>
            <result property="comId" column="com_id"/>
            <result property="userIdFrom" column="user_id_from"/>
            <result property="userIdTo" column="user_id_to"/>
            <association property="userFrom" javaType="cn.zdxh.assistant.pojo.UserCustomer">
                <id property="id" column="uid"/>
                <result property="userLoginname" column="user_loginname"/>
                <result property="userImage" column="user_image"/>
            </association>
        </collection>

    </resultMap>
</mapper>

mybatis-config.xml mybatis的配置文件,开启驼峰命名法

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
</configuration>

使用mybatis的时候不要忘记开启mapper扫描

启动类中 SpringBootAssistantApplication
@MapperScan("cn.zdxh.assistant.mapper")//扫描mapper接口,相当于给每个类添加@Repository
@SpringBootApplication
public class SpringBootAssistantApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootAssistantApplication.class, args);
	}
}

application.properties配置文件

#配置mysql数据库
spring.datasource.url= jdbc:mysql://localhost:3306/zdxh_assistant
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
#配置spring data jpa,已经默认开启驼峰命名法
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
#配置mybatis的配置文件以及映射文件
mybatis.config-location=classpath:/mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:/mybatis/mapper/*.xml
#修改访问端口
server.port=8090

Android端 :

项目结构

activity:放置activity文件

adapter:放置适配器adapter文件

base:放置一些baseActivity或者baseFragment文件

bean:放置模型类,这里的模型类一定要实现Serializable接口,给页面传对象的时候Bundle需要序列化

fragment:放置fragment文件

utils:工具类

layout:放置布局文件

 

主程序入口

MainActivity
public class MainActivity extends BaseActivity {

    private ViewPager viewpager;
    private TabLayout tableLayout;
    private List<Fragment> mFragments;
    private List<String> titleDatas;
    private ImageView addView;

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        initView();
        //初始化监听器
        initListener();
        //初始化Tab
        initTab();
        //初始化Fragment
        initFragment();
    }
    public void initView(){
        viewpager=findViewById(R.id.vp_main);
        tableLayout=findViewById(R.id.tl_main);
        addView=findViewById(R.id.iv_add);
    }
    //右上角的加号控件
    public void initListener(){
        addView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 列表对话框
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle("选择要发布的信息")
                            .setItems(new String[]{"发布树洞","发布拼饭/伞"}, null)
                            .show();

            }
        });
    }
    //fragment中的tab要显示的内容
    public void initTab(){
        titleDatas=new ArrayList<String>();
        titleDatas.add("树洞");
        titleDatas.add("拼友");
        titleDatas.add("我的");
    }
    //初始化Fragment
    public void initFragment(){
        mFragments = new ArrayList<Fragment>();
        mFragments.add(new InvitationFragment());
        mFragments.add(new PublishFragment());
        mFragments.add(new MyFragment());
        //初始化adapter
        MainFragmentAdapter adapter = new MainFragmentAdapter(getSupportFragmentManager(), mFragments,titleDatas);
        tableLayout.setupWithViewPager(viewpager);
        // 将适配器和ViewPager结合
        viewpager.setAdapter(adapter);

    }



}

activity_main.xml布局文件 

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/colorPrimary">

        <TextView
            android:layout_width="100dp"
            android:layout_height="30dp"
            android:layout_marginLeft="20dp"
            android:layout_centerVertical="true"
            android:textSize="17sp"
            android:textColor="#ffffff"
           android:text="新华小助手"/>
        <ImageView
            android:id="@+id/iv_add"
            android:layout_alignParentRight="true"
            android:layout_marginRight="20dp"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_centerVertical="true"
            android:background="@drawable/add"/>

    </RelativeLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_main"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="10">
    </android.support.v4.view.ViewPager>

   <android.support.design.widget.TabLayout
       android:id="@+id/tl_main"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1">

   </android.support.design.widget.TabLayout>


</LinearLayout>
MainFragmentAdapter适配器
public class MainFragmentAdapter extends FragmentPagerAdapter {

    private List<Fragment> mFragments;
    private List<String> titleDatas;

    //构造器,传入必须的FragmentManager对象,以及Fragment和Tab
    public MainFragmentAdapter(FragmentManager fm, List<Fragment> mFragments,List<String> titleDatas) {
        super(fm);
        this.mFragments=mFragments;
        this.titleDatas=titleDatas;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titleDatas.get(position);
    }
}

 fragment_invitation.xml文件,三大fragment之一,树洞的fragment布局文件

<?xml version="1.0" encoding="utf-8"?>
<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=".fragment.InvitationFragment">


    <ListView
        android:id="@+id/lv_invs"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


</LinearLayout>

fragment_invitation_item.xml中listView中的item文件 

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

        <TextView
            android:id="@+id/tv_invs"
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:textSize="20sp"
            android:textColor="#000000"
           android:text="新华小助手"/>

</LinearLayout>

fragment_my.xml 和fragment_publish.xml两个文件差不多,三大fragment之一

<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".fragment.MyFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="这是我的页面"/>

</FrameLayout>
MyFragment和PublishFragment差不多,没写数据
public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_my, container, false);
    }

}

InvitationFragment,数据写在这个页面

public class InvitationFragment extends Fragment {

    private int INVITATIONS=1;
    private List<Invitation> invitations;
    private ListView listView;

    @SuppressLint("HandlerLeak")
    Handler handler=new Handler() {
        @Override
        public void handleMessage(Message message) {
            super.handleMessage(message);
            if (message.what == INVITATIONS){
                //将请求所得数据传进适配器
                InvitationsAdapter invitationsAdapter=new InvitationsAdapter(invitations,getContext());
                listView.setAdapter(invitationsAdapter);
                //初始化监听器,传入树洞的id,然后在另外一个activity就根据这个id查询树洞详情
                initListener();
            }
        }
    };

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //初始化控件
        View view = inflater.inflate(R.layout.fragment_invitation, container, false);
        listView=view.findViewById(R.id.lv_invs);
        invitations=new ArrayList<>();

        //请求获取所有的树洞,可以分页
        queryInvitations();

        return view;
    }

    public void initListener(){
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent=new Intent(getActivity(), InvitationDetailActivity.class);
                Bundle bundle=new Bundle();
                Invitation invitationBudle = invitations.get(position);
                bundle.putSerializable("invitation",invitationBudle);
                intent.putExtras(bundle);
                startActivity(intent);
            }
        });
    }


    public void queryInvitations() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //请求数据
                String json = OkHttpUtils.OkHttpGet("http://47.107.126.98:8090/invs/0/20");
                //Gson解析json数据
                Gson gson = new Gson();
                PageIns pageIns = gson.fromJson(json, PageIns.class);
                invitations=pageIns.getContent();
                //发送消息,不能在子线程更新UI
                Message message = Message.obtain();
                message.obj = invitations;
                message.what = INVITATIONS;
                handler.sendMessage(message);
            }
        }).start();
    }
}
InvitationsAdapter适配器
public class InvitationsAdapter extends BaseAdapter {

    private List<Invitation> invitations;
    private Context mContext;

    public InvitationsAdapter(List<Invitation> invitations, Context mContext) {
        this.invitations = invitations;
        this.mContext=mContext;
    }

    @Override

    public int getCount() {
        return invitations.size();
    }

    @Override
    public Object getItem(int position) {
        return invitations.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view=null;
        if (convertView==null){
            //复用item对象
           view= LayoutInflater.from(mContext).inflate(R.layout.fragment_invitation_item,parent,false);
        }else {
            view=convertView;
        }
        TextView textView=view.findViewById(R.id.tv_invs);
        //ImageView imageView=view.findViewById(R.id.iv_invs);
        textView.setText(invitations.get(position).getInvContent());
        return view;
    }
}

OkHttpUtils工具类,用来url请求 ,这里是同步的请求,还有一种异步的请求

public class OkHttpUtils {

    public static String OkHttpGet(String url){
        OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .get()//get请求
                .build();
        final Call call = okHttpClient.newCall(request);
        String json="";
        try {
            Response response = call.execute();
            json=response.body().string();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return json;
    }
}

PageIns这个是用来介绍分页后Invitation的

public class PageIns implements Serializable {
    private List<Invitation> content;

    public List<Invitation> getContent() {
        return content;
    }

    public void setContent(List<Invitation> content) {
        this.content = content;
    }
}

Comment、Invitation、Reply、User和上面的CommentCustomer、InvitationCustomer、ReplyCustomer、UserCustomer内容都一样的,只不过新增实现了接口Serializable接口

InvitationDetailActivity是点击树洞进去之后的activity,树洞详情

public class InvitationDetailActivity extends BaseActivity {

    private static int COMMENTS=1;
    private List<Comment> comments;
    private Invitation invitation;
    private TextView invContent;
    private RecyclerView recyclerView;

    @SuppressLint("HandlerLeak")
    Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //如果属于数据请求的消息,更新UI
            if (msg.what==COMMENTS){
                LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
                recyclerView.setLayoutManager(layoutManager);
                //设置监听器
                InvitationDetailAdapter adapter = new InvitationDetailAdapter((List<Comment>)msg.obj);
                adapter.setItemClickListener(new InvitationDetailAdapter.OnItemClickListener() {
                    @Override
                    public void onItemClick(int position) {
                        // Toast.makeText(getApplicationContext(),"您点击了"+position+"行", Toast.LENGTH_SHORT).show();
                        Intent intent=new Intent(getApplicationContext(),CommentDetailActivity.class);
                        Bundle bundle=new Bundle();
                        //包装对象传给第二个Activity
                        Comment commentBundle = comments.get(position);
                        bundle.putSerializable("comment",commentBundle);
                        intent.putExtras(bundle);
                        startActivity(intent);
                    }
                });

                recyclerView.setAdapter(adapter);
            }
        }
    };

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_invitation_detail);
        initView();
        initData();
    }

    public void initView(){
        invContent=findViewById(R.id.tv_inv);
        recyclerView=findViewById(R.id.rv_inv);
    }

    public void initData(){
        //获取上一个页面fragment传来的对象
        Intent intent=getIntent();
        Bundle bundle = intent.getExtras();
        invitation=(Invitation) bundle.getSerializable("invitation");
        //有数据后更新UI
        invContent.setText(invitation.getInvContent());

        int invId = invitation.getId();
        comments=new ArrayList<Comment>();
        //请求树洞详情
        queryInvitationDetail(invId);
    }

    public void queryInvitationDetail(final int iId){
        new Thread(new Runnable() {
            @Override public void run() {
                //请求数据
                String json= OkHttpUtils.OkHttpGet("http://47.107.126.98:8090/inv/"+iId);
                //Gson解析json数据
                Gson gson=new Gson();
                comments = gson.fromJson(json, new TypeToken<List<Comment>>(){}.getType());
                //发送消息,不能在子线程更新UI
                Message message=Message.obtain();
                message.obj=comments;
                message.what=COMMENTS;
                handler.sendMessage(message);
            }
        }).start();//不要忘记start线程

    }
}
树洞详情的recycleView适配器的InvitationDetailAdapter
public class InvitationDetailAdapter extends RecyclerView.Adapter<InvitationDetailAdapter.ViewHold>{

    private List<Reply> replies;
    private List<Comment> comments;
    private int position;

    //点击事件的接口
    private OnItemClickListener mItemClickListener;

    /**
     * 由于recycleView没有监听器,需要自定义监听器接口
     */
    public interface OnItemClickListener{
        void onItemClick(int position);
    }

    public void setItemClickListener(OnItemClickListener itemClickListener) {
        mItemClickListener = itemClickListener;
    }

    //构造函数
    public InvitationDetailAdapter(List<Comment> comments) {
        this.comments = comments;
    }



    public class ViewHold extends RecyclerView.ViewHolder{
        private RecyclerView recyclerView;
        private TextView textView;
        public ViewHold(View itemView) {
            super(itemView);
            textView=(TextView)itemView.findViewById(R.id.tv_inv_info);

            //嵌套RecycleView使用
            recyclerView=itemView.findViewById(R.id.rv_inv_nest);
            LinearLayoutManager layoutManager = new LinearLayoutManager(itemView.getContext());
            layoutManager.setAutoMeasureEnabled(true);
            recyclerView.setLayoutManager(layoutManager);
            //方法加载顺序的问题
            if (replies==null){
                //第一次加载的时候
                replies=comments.get(position).getReplys();
            } else {
                //第二次往后的加载
                replies=comments.get(position+1).getReplys();
            }

            //设置第二个recycleView的是配置
            NestInvitationDetailAdapter adapter = new NestInvitationDetailAdapter(replies);
            recyclerView.setAdapter(adapter);


        }
    }
    @Override
    public ViewHold onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.invitation_layout,parent,false);
        ViewHold viewHold = new ViewHold(view);
        if( mItemClickListener!= null){
            view.setOnClickListener( new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //获取传值过来的position
                    mItemClickListener.onItemClick((int)v.getTag());
                }
            });
        }
        return viewHold;
    }

    @Override
    public void onBindViewHolder(ViewHold holder, int position) {
        Comment comment = comments.get(position);
        this.position=position;
        // Log.e("哈哈哈哈哈哈哈",position+"");//从0开始逐渐递增
         holder.textView.setText(comment.getComContent());
         //把item的position传给onItemClick
         holder.itemView.setTag(position);
    }


    @Override
    public int getItemCount() {
        return comments.size();
    }

}

嵌套的Recycleview的适配器 

NestInvitationDetailAdapter
public class NestInvitationDetailAdapter extends RecyclerView.Adapter<NestInvitationDetailAdapter.ViewHold> {

    private List<Reply> replies;

    public NestInvitationDetailAdapter(List<Reply> replies) {
        this.replies = replies;
    }

    public static class ViewHold extends RecyclerView.ViewHolder{
        private TextView replyContent;
        private TextView replyContent2;
        public ViewHold(View itemView) {
            super(itemView);
            replyContent=(TextView)itemView.findViewById(R.id.tv_inv_info_nest);
            replyContent2=(TextView)itemView.findViewById(R.id.tv_inv_info_nest2);
        }
    }
    @Override
    public ViewHold onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.invitation_layout_nest,parent,false);
        ViewHold viewHold = new ViewHold(view);
        return viewHold;
    }

    @Override
    public void onBindViewHolder(ViewHold holder, int position) {
        //Log.e("呵呵呵呵呵呵呵",position+"");
        if (position==0){
            //代表下拉
            if (replies.size()>=2){
                //回复多于两条的时候
                holder.replyContent.setText(replies.get(position).getReContent());
                holder.replyContent2.setText(replies.get(position+1).getReContent());
            }else if(replies.size()==1){
                //只有一条回复
                holder.replyContent.setText(replies.get(position).getReContent());
            }
        }else if (position==1){
            //代表上划
            if (replies.size()>2){
                holder.replyContent.setText(replies.get(position-1).getReContent());
                holder.replyContent2.setText(replies.get(position).getReContent());
            }else if(replies.size()==1){
                holder.replyContent.setText(replies.get(position-1).getReContent());
            }
        }

    }


    @Override
    public int getItemCount() {
        return replies.size();
    }
}

第一个recycleView的页面,activity_invitation_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<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=".fragment.InvitationFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:id="@+id/tv_inv"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:textSize="18sp"
        android:textColor="#000000"
        android:text="这是评论详情页面" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_inv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />



</LinearLayout>

嵌套的recycleView页面 invitation_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_inv_info"
        android:layout_width="match_parent"
        android:layout_height="50dp" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_inv_nest"
        android:layout_width="match_parent"
        android:layout_marginLeft="30dp"
        android:layout_height="50dp"
        android:background="#b1b1b1"/>

</LinearLayout>

对应的item文件 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_inv_info_nest"
        android:layout_width="match_parent"
        android:layout_height="25dp" />
    <TextView
        android:id="@+id/tv_inv_info_nest2"
        android:layout_width="match_parent"
        android:layout_height="25dp" />

</LinearLayout>

评论详情CommentDetailActivity

public class CommentDetailActivity extends BaseActivity {

    private Comment comment;
    private ListView listView;
    private TextView comContent;


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_comment_detail);
        initView();
        initData();
    }

    public void initData(){
        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        //获取上一个activity过来的对象
        comment=(Comment) bundle.getSerializable("comment");
        //部分值直接更新UI
        comContent.setText(comment.getComContent());
        //部分值传到listView上
        CommentDetailAdapter commentDetailAdapter=new CommentDetailAdapter(comment.getReplys(),getApplicationContext());
        listView.setAdapter(commentDetailAdapter);
    }

    public void initView() {
        comContent = findViewById(R.id.tv_com);
        listView = findViewById(R.id.ll_com);
    }


}

对应的listView适配器CommentDetailAdapter 

public class CommentDetailAdapter extends BaseAdapter {

    private List<Reply> replies;
    private Context mContext;

    public CommentDetailAdapter(List<Reply> replies, Context mContext) {
        this.replies = replies;
        this.mContext=mContext;
    }

    @Override

    public int getCount() {
        return replies.size();
    }

    @Override
    public Object getItem(int position) {
        return replies.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view=null;
        if (convertView==null){
            //复用view,不会一直创建item,保持高性能
           view= LayoutInflater.from(mContext).inflate(R.layout.activity_comment_detail_item,parent,false);
        }else {
            view=convertView;
        }
        TextView textView=view.findViewById(R.id.tv_com_item);
        textView.setText(replies.get(position).getReContent());
        return view;
    }
}

activity_comment_detail.xml评论详情布局文件

<?xml version="1.0" encoding="utf-8"?>
<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=".activity.CommentDetailActivity">

    <TextView
        android:id="@+id/tv_com"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:textSize="18sp"
        android:textColor="#000000" />

    <ListView
        android:id="@+id/ll_com"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </ListView>

</LinearLayout>

activity_comment_detail_item.xml 评论详情listView的item布局文件 

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">


        <TextView
            android:id="@+id/tv_com_item"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="20sp"
            android:textColor="#000000"
           android:text="评论回复"/>


</LinearLayout>

 总结:

交互最重点:

发送json--->@ResponseBody

解析json---->Gson gson=new Gson();
               List<Commennt>  comments = gson.fromJson(json, new TypeToken<List<Comment>>(){}.getType());

猜你喜欢

转载自blog.csdn.net/weixin_38802061/article/details/84348442