尝试用kotlin做一个app(十四)

发布帖子和注册功能实现

先做发布帖子的

发布帖子页面布局

布局页面做成这个样子了

1.写布局文件的时候遇到问题

需要改变EditText下滑线的颜色和粗细

改变颜色简单,参考Android 更改EditText下划线的颜色样式

自定义一个样式

<style name="MyEditText" parent="Theme.AppCompat.Light">
        <item name="colorControlNormal">#e6e6e6</item>
        <item name="colorControlActivated">#c6c6c6</item>
    </style>

在布局中引入

android:theme="@style/MyEditText"

但是这种方法不知道怎么改变下划线的粗细,那试着设置了一下EditText的背景

比如定义一个样式

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="#e6e6e6" />
        </shape>
    </item>
    <item android:bottom="1dp">
        <shape>
            <solid android:color="#Fafafa" />
        </shape>
    </item>
</layer-list>

引入样式

android:background="@drawable/bg_line_bottom"

2.处理内容输入EditText

 主要是要往里面插入图片。。参考的是实现在edittext中任意插入图片

 那篇文章关于打开系统摄像头的方法大概有bug,而且我是在模拟器上测试的,没有摄像头功能,所以还是先去掉拍摄功能,点击上传图片直接打开系统的相册。关于打开摄像头拍照这个之后有时间专门来研究下

        post_img.setOnClickListener {
            var items=Array<CharSequence>(2){""}
            items[0] = "手机相册"
            items[1] = "相机拍摄"
            var dlg=AlertDialog.Builder(this).setTitle("选择图片").setItems(items,object:DialogInterface.OnClickListener{
                override fun onClick(dialog: DialogInterface?, which: Int) {
                    when(which){
                        0->{
                            //println("click item 0")
                            var getImage=Intent(Intent.ACTION_GET_CONTENT)
                            getImage.addCategory(Intent.CATEGORY_OPENABLE)
                            getImage.setType("image/*")
                            startActivityForResult(getImage,1)

                        }
                        1->{
                            //println("click item 1")
                            //var getImageByCamera= Intent("android.media.action.IMAGE_CAPTURE")
                            //startActivityForResult(getImageByCamera,0)

                        }
                    }
                }
            }).create()
            dlg.show()
        }

那其实弹出对话框也没有必要了

post_img.setOnClickListener {
var getImage = Intent(Intent.ACTION_GET_CONTENT)
getImage.addCategory(Intent.CATEGORY_OPENABLE)
getImage.type = "image/*"
startActivityForResult(getImage, 1)

}

然后我发现我也没有自定义EditText了,直接在activity中重写onActivityResult方法吧

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode==PHOTO_SUCCESS){
var originUri=data?.data

//创建bitmap
var originBitmap=BitmapFactory.decodeStream(contentResolver.openInputStream(originUri!!))

if(originBitmap!=null){
//设置spannableString
var imageSpan= ImageSpan(this,originBitmap)
var spannableString=SpannableString("[local][local]")//[local]1[local]没啥用
spannableString.setSpan(imageSpan,0,"[local]".length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

//将图片插入光标位置
val index=post_content.selectionStart
val editText=post_content.editableText
if(index<0||index>=editText.length){
editText.append(spannableString)
}else{
editText.insert(index,spannableString)
}

}else{
println("从相册读取图片失败!!")
}

}
}

还可以新建一个函数设置插入图片的大小。但是好像不设置大小也可以,图片会自适应屏幕的大小

关于SpannableString可以参考

SpannableString和SpannableStringBuilder总结

 接下来要考虑怎么把编辑好的帖子发布出去,也主要是处理图片的上传了。关于图片的上传,貌似有两种方法,一种是上传图片的二进制流,另一种是将图片转换成base64编码。。网上有很多介绍图片上传的,我还是不看别的,自己想想办法看

我这里试试把图片转换成base64编码吧

再来看一下这段代码

if(requestCode==PHOTO_SUCCESS){
            var originUri=data?.data

            //创建bitmap
            var originBitmap=BitmapFactory.decodeStream(contentResolver.openInputStream(originUri!!))if(originBitmap!=null){
                //设置spannableString
                var imageSpan= ImageSpan(this,originBitmap)
                var spannableString=SpannableString("[local][local]")
                spannableString.setSpan(imageSpan,0,"[local]".length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

                //将图片插入光标位置
                val index=post_content.selectionStart
                val editText=post_content.editableText
                if(index<0||index>=editText.length){
                    editText.append(spannableString)
                }else{
                    editText.insert(index,spannableString)
                }

            }

貌似我只要把originBitmap转换成base64就可以了,然后再把

var spannableString=SpannableString("[local][local]")

改成

<img src='data:image/jpeg;base64,'+base64ImgData+"'/>

具体是这样的

var byteData:ByteArray=contentResolver.openInputStream(originUri!!)!!.readBytes()
            //var base64Encoder= Base64.getEncoder()
            //var base64ImgString=base64Encoder.encodeToString(byteData)
            var base64ImgString=Base64.encodeToString(byteData,Base64.NO_WRAP)


            if(originBitmap!=null){
                //设置spannableString
                var imageSpan= ImageSpan(this,originBitmap)
                //var spannableString=SpannableString("[local][local]")
                var imgStr=base64ImgString
                var spannableStringRes="<img src='data:image/jpeg;base64,"+imgStr+"'/>"
                var spannableString=SpannableString(spannableStringRes)
                spannableString.setSpan(imageSpan,0,spannableStringRes.length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

这样我测试了一下,输出的base64图片编码,是可以直接显示出图片的

3.处理选择分类

就是在当前页面点击“必选”旁边的箭头,跳转到展示所有分类的页面,然后点击分类页面的条目,返回分类给当前的页面。继续用startActivityForResult吧。这个写过太多遍,~每多写一次,布局就变好看一点不知道为什么~代码就不贴了

 点击选择之后

 4.点击发布按钮,上传到数据库

那似乎要把标题,内容,分类这些数据封装成一个json。我这不想用第三方的工具了,直接建一个类,然后用它的toString方法吧

类似这样吧

class PostMsg {
    var title:String?=null
    var content:String?=null
    var pubdate:Long?=null
    var category:String?=null
    var username:String?=null
    constructor(title:String?,content:String?,pubdate:Long?,category:String?,username:String?){
        this.title=title
        this.content=content
        this.pubdate=pubdate
        this.category=category
        this.username=username

    }
  override fun toString(): String {
  return "{title='$title', content='$content', category='$category', username='$username'}"

  }
 }

 kotlin提示convert to primary constructor,转换一下就成这样了

class PostMsg(
    var title: String?,
    var content: String?,
    var pubdate: Long?,
    var category: String?,
    var username: String?
) {
  override fun toString(): String {
  return "{title='$title', content='$content', category='$category', username='$username'}"

  }
 }

 然后设置发布按钮的点击监听

toolbar_action.setOnClickListener {
            var title=post_title.text.toString()
            var category=post_category.text.toString()
            var content=post_content.text.toString()
            var sp=getSharedPreferences("CurrentUserInfo", Context.MODE_PRIVATE)
            var username=sp.getString("username","null")

            var postMsg:PostMsg= PostMsg(title,content,category,username)

            var client:OkHttpClient= OkHttpClient()
            var mediaType= "text/html;charset=urf-8".toMediaTypeOrNull()
            var requestBody=postMsg.toString()
            var body= requestBody.toRequestBody(mediaType)
            var request:Request=Request.Builder().post(body).url(url).build()
            Thread(object:Runnable {
                override fun run() {
                    var response:Response=client.newCall(request).execute()
                }
            }).start()

        }

当然发送post用的是同步请求,用异步会更好,点击提交,结束当前activity,列表显示帖子正在发布,直到请求完成,显示帖子发布成功;另外返回结果也可以处理一下,但是我已经无力了...

在后端在做一个请求的api

     req.setCharacterEncoding("utf-8");
        ServletInputStream in =req.getInputStream();
        byte[] originBytes=in.readAllBytes();
        String originStr=new String(originBytes,"utf-8");
        //System.out.println(originStr);

        JSONObject JsonStr=JSONObject.fromObject(originStr);
        String title=JsonStr.getString("title");
        String content=JsonStr.getString("content");
        String category=JsonStr.getString("category");
        String username=JsonStr.getString("username");

        Timestamp pubdate=new Timestamp(System.currentTimeMillis());

会出现一个错误

net.sf.json.JSONException: Expected a ',' or '}' at character 36 of {title=

这意味着帖子内容,里面有一些字符不能转换成json。那我就把content转换成Url编码吧!!!

在客户端编码

var contentUrlEncode=URLEncoder.encode(content,"utf-8")

服务器端解码

content= URLDecoder.decode(content,"utf-8");

还有个问题,是我之前建post表的时候,弄了一个thumb字段,表示缩略图,在后台添加帖子,可以直接上传缩略图,但是客户端发表帖子,貌似要把内容里的第一张图片设置为缩略图

比如,在后台我获得的帖子内容是这样

分享一张图片
<img src='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2w
...........QD0IzUR1W4Mu7djtwKgoojhaMfhig5UTDUrhT8sjc9c81G91K+753+Y5ILU2itVTgndIXKhAWz0pd3PSiiqsith3mf7NHmf7NNoo5UA7zP9mmjgUUUwCiiigAooooAKKKKACiiiszMKKKKACiiigAPWiiiqiVEKKKKooKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Z'/>啦啦啦

直接保存图片的base64编码貌似也行,但是我又设置了thumb字段的最大长度是100,存不下。所以只好把它转换成二进制流,写入服务器thumb文件夹,并且获得它的相对地址,作为thumb的值

int startPos=content.indexOf("data:image/jpeg;base64,")+"data:image/jpeg;base64,".length();
        int endPos=content.indexOf("'/>");
        String base64ImgStr=content.substring(startPos,endPos);
        //System.out.println(base64ImgStr);
        byte[] imgBytes= Base64.getDecoder().decode(base64ImgStr);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String ymd = sdf.format(new Date());
        String fileName = ymd + ".jpg";

        String savePath=req.getSession().getServletContext().getRealPath("/")+"../thumb/";
        FileOutputStream out=new FileOutputStream(savePath+fileName);
        out.write(imgBytes);
        out.close();
        String thumb="/thumb/"+fileName;
        //System.out.println(thumb);

竟然一次成功了。。。但是这样不安全,要是内容里面本身有“data:image/jpeg;base64,”这样的字符呢?所以其实一开始的时候,或许不应该把图片转成base64编码,而应该直接传图片的二进制流...

最后把值插入数据库就行。。

 还有输入检查没有做

 运行

 

猜你喜欢

转载自www.cnblogs.com/vocus/p/12529594.html