kotlin android 踩坑

   Toast.makeText(上下文对象, "这是你要提示的内容", 提示时间).show()
    消息提示       

  第一个参数 上下文参数,指当前显示页面,kotlin中使用this@页面名代替java中的    页面名.this
  第二个参数 你需要提示的内容
  第三个参数  显示时间的长短  Toast.LENGTH_SHORT  Toast.LENGTH_LONG
  创建Toast后需要使用show显示出来

   Toast.makeText(this@MainActivity, "这是你要提示的内容", Toast.LENGTH_SHORT).show()

示例:




   

    

    



package com.example.chenle.bar

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        rbRating.max = 100
        rbRating.progress = 20
        rbRating.setOnRatingBarChangeListener {
            ratingBar, rating, _ ->
            val progress = ratingBar.progress
            Toast.makeText(this@MainActivity, "progress:" + progress + "rating" + rating, Toast.LENGTH_SHORT).show()

        }
    }
}

LinearLayout设置圆角

示例


kotlin android 踩坑_第1张图片
圆角边框

在drawable下面新建一个xml文件
然后代码




    

    

    
    


然后在LinearLayout中引入background属性


kotlin android 踩坑_第2张图片
image.png

错误:Can't create handler inside thread that has not called Looper.prepare()

原因: toast的实现需要在activity的主线程才能正常工作,所以传统的非主线程不能使toast显示在actvity上,通过Handler可以使自定义线程运行于Ui主线程。

解决方案:

Looper.prepare();
Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_LONG).show();
Looper.loop();

错误:CLEARTEXT communication to X.X.X.X not permitted by network security policy

原因:由于 Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉

OkHttp3 做了检查,所以如果使用了明文流量,默认情况下,在 Android P 版本 OkHttp3 就抛出异常: CLEARTEXT communication to " + host + " not permitted by network security policy

解决方案:
在 res 下新建一个 xml 目录,然后创建一个名为:network_security_config.xml 文件 ,该文件内容如下:



  

然后在 AndroidManifest.xml application 标签内应用上面的xml配置:

全局获取context对象

最近做了个需求需要使用到sharePrefrence,但是需要抓到context对象或者activity对象。但是究其原因,我们需要拿到application的context上下文,所以我们全局写一个继承Application的类抓取context对象
BaseApplication.kt

package com.gala.gala

import android.app.Application

class BaseApplication : Application() {
  override fun onCreate() {
    super.onCreate()
    context = this
  }

  companion object {
    lateinit var context: GalaApplication
      private set
  }
}

在AndroidManifest.xml中



 
  ...
   

 


这样我们就可以用BaseApplication.context在全局其他地方抓到context对象然后进行sp储存

val context = GalaApplication.context

问题:在进行sp的Set集合存储的时候发现存储进去了刷新后读出来内部数据仍然为空,

Note that you must not modify the set instance returned by this call. 
The consistency of the stored data is not guaranteed if you do, 
nor is your ability to modify the instance at all.

getStringSet的object和putStringSet的object不能是同一个,不能在get之后,进行更改,然后又put进去,这样是无法更改的

我们在putStringSet的时候new一个新的对象或者我们在进行储存
或者我们在putStringSet的时候直接clear()一下

 editor.clear()
 editor.putStringSet(name, value)

问题: java.io.IOException(Cleartext HTTP traffic to XXX not permitted) 或者 java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by network security policy

原因: Android P设备无法用http非加密明文进行网络请求,https不受影响。
方案:
1 改用https请求
2 targetSdkVersion 降到27以下
3 更改安全配置
在res创建xml文件夹,创建network_security_config.xml文件



  

导入AndroidManifest.xml


通过R.drawable获取到真实的资源路径,然后使用glide加载

if (string.contains("R.drawable")) {
          val resId = resources.getIdentifier(string.replace("R.drawable.", ""),
              "drawable", context.packageName)

          avatarImageView.setImageResource(resId)
        } else {
          Glide.with(this@Fragment1)
              .load(string)
              .apply(RequestOptions().placeholder(R.drawable.aurora_headicon_default))
              .into(avatarImageView)
        }

button更改drawer

 val mRouteOnDraw = ContextCompat.getDrawable(context, drawerId)
    mRouteOnDraw.setBounds(0, 10, 50,60)
    buttonName.setCompoundDrawables(null, mRouteOnDraw, null, null)

这里注意一定要给drawer资源进行setBounds操作,这样才能够成功设置drawer资源

错误: okhttp3.internal.http.RealResponseBody@cc75822

我们使用response.body()?.string()代替response.body().toString()

注意:response.body()在被调用后,http请求就关闭了,所以response.body只能被调用一次。

edittext光标高度问题

有时候edittext光标的高度大于我们text的输入高度,如果想要调小我们需要自定义文件

android:textCursorDrawable="@drawable/cursor"

cursor.xml



  
  
  

tablayout和viewPage的问题

当在加载的时候,viewpage有预加载的功能,也就是如果有三个tab的话,第一个tab加载的时候会把第二个tab预加载出来,但是我从第一个tab直接跳到第三个tab会自动刷新数据,如果我限定数据存在时候不再刷新的话,这个时候从第一个页面跳到第三个会没有数据显示。

方法

viewPage.offscreenPageLimit = 3

限制三个页面一起预加载。再进行是否渲染数据的判断

如果你想取消预更新,可以参考这两篇文章

https://blog.csdn.net/chenzheng8975/article/details/54645704
https://www.jianshu.com/p/66ff0330f2d9
https://www.jianshu.com/p/eb81f3692229

错误Error parsing XML: not well-formed (invalid token)

解决办法:找到对应报错的XML,然后看看自己哪里写错了多手了!!!

更改LinearLayout的宽度(当高宽是wrap_content)

FrameLayout.LayoutParams layoutParams;
layoutParams = getLayoutParams(LayoutParams.WRAP_CONTENT, height, top);

 TextView textView;
textView = new TextView(_registerNewMealActivity);
textView.setText(text);
textView.setLayoutParams(layoutParams);

错误: android.widget.FrameLayoutLayoutParams cannot be cast to android.widget.RelativeLayout$LayoutParams

我现在做的结构如下

kotlin android 踩坑_第3张图片
image.png
错误代码
 val params = FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.MATCH_PARENT,
        Gravity.TOP)
relative_layout_ccc.layoutParams = params
val imageView = ImageView(context)
imageView.setImageDrawable(getDrawerResource(R.drawable.hashiqi))
val lp = FrameLayout.LayoutParams(FrameLayout.LayoutParams(200, 200))
imageView.layoutParams = lp
fragment_frame_ccc.addView(imageView)
改正代码
 val params = RelativeLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.MATCH_PARENT)
relative_layout_ccc.layoutParams = params
val imageView = ImageView(context)
imageView.setImageDrawable(getDrawerResource(R.drawable.hashiqi))
val lp = FrameLayout.LayoutParams(FrameLayout.LayoutParams(200, 200))
imageView.layoutParams = lp
fragment_frame_ccc.addView(imageView)
参考https://stackoverflow.com/questions/11544964/framelayout-to-relativelayout-classcastexception-even-if-there-is-no-framelayout
FrameLayout.LayoutParams _rootLayoutParams = new FrameLayout.LayoutParams(_rootLayout.getWidth(), _rootLayout.getHeight());
_rootLayoutParams.setMargins(300, 0, 300, 0);
_rootLayout.setLayoutParams(_rootLayoutParams);

recycleview出现了数据错乱的问题。

使用recycleview填充数据的时候,在滑动过程中发生数据重叠的问题。原因是recycleview存在复用的问题,参考https://www.jianshu.com/p/697ce543b1c1
解决 在设置adapter的时候加入adapter.setHasStableIds(true),用于回收复用机制中,给 ViewHoler 设置一个唯一的标识符
adapter.setHasStableIds(true)
chat_ai_recycle_view.adapter = adapter

问题:RecycleView滑动的时候出现了数据错乱的问题。上滑下滑前后数据不同

解决:由于RecyclerView的onBindViewHolder()方法,只有在getItemViewType()返回类型不同时才会调用,所以我们为了让他每次调用onBindViewHolder()方法,我们需要重写getItemViewType()并将每次的position return回去
 override fun getItemViewType(position: Int): Int {
    return position
  }
在viewHolder中动态添加image
val imageView = ImageView(context)
imageView.setImageDrawable(getDrawerResource(R.drawable.hashiqi))
val lp = FrameLayout.LayoutParams(FrameLayout.LayoutParams(200, 200))
 imageView.layoutParams = lp
holder.itemView.chat_item_receive_fragment_frame.addView(imageView)
recycleview固定在底部
val layoutManager = LinearLayoutManager(context)
layoutManager.stackFromEnd = true
recycle_view.layoutManager = layoutManager

问题: 当我们加入一条数据后,希望继续固定在底部

在我们触发发送后端处理的时候设置recycleView的scrollToPosition方法
edit_text.setOnEditorActionListener{ _, actionId, _ ->
  if (actionId == EditorInfo.IME_ACTION_SEND) {
      //隐藏键盘
      hideSoftKeyBoard()
      //后端在这里处理数据
      chat_ai_recycle_view.scrollToPosition(presenter.messageList.size - 1)
      chat_ai_edit_text.text = null
  }
  true
}

问题:EditText多行和监听键盘事件

描述:今天碰到EditText的问题。当inputType为textMultiLine的时候,可以换行但是发现监听更改键盘事件无效。inputType为text的时候却是单行。

原因:在inputType = textMultiLine 时,点击回车键的默认操作就是换行,不会有其他的事件触发
解决:inputType为text,在代码中添加
chat_ai_edit_text.maxLines = 3
chat_ai_edit_text.setHorizontallyScrolling(false)
监听键盘发送事件
chat_ai_edit_text.setOnEditorActionListener { _, actionId, _ ->
      if (actionId == EditorInfo.IME_ACTION_SEND) {
        hideSoftKeyBoard()
        //在这里进行数据处理
      chat_ai_recycle_view.scrollToPosition(presenter.messageList.size - 1)
        chat_ai_edit_text.text = null
      }
      false
    }

问题:想在fragment中调用activity的presenter对象。使得数据保持一致。

思路:在fragment中写一个函数,在activity中调用并将presenter对象传入。
  fun newInstance(presenter: ChatAiMessageListPresenter) {
    this.presenter = presenter
  }

在activity中

val beginTransaction = fragmentManager!!.beginTransaction()
        val editSendNumber = EditSendNumber()
        editSendNumber.newInstance(presenter)
        beginTransaction.replace(R.id.chat_ai_fragment_frame, editSendNumber)
        beginTransaction.commit()

textview显示两行,其余的使用...代替

 android:lines="2"
android:ellipsize="end"

去除recycleview的上拉下拉阴影和右边侧滑栏

  
  

imageview设置uri图片

image_view.setImageURI(Uri.fromFile(File(本地文件路径)))

获取动态权限

  //动态申请权限
  private fun applyWriteExternalStoragePermission() {
    val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
    ActivityCompat.requestPermissions(this, permissions, 0)
  }

//WRITE_EXTERNAL_STORAGE权限
  private fun hasWriteExternalStoragePermission(): Boolean {
    val result =  ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
    return result == PackageManager.PERMISSION_GRANTED
  }

//READ_EXTERNAL_STORAGE权限
  private fun hasReadExternalStoragePermission(): Boolean {
    val result =  ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
    return result == PackageManager.PERMISSION_GRANTED
  }

  override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//有权限的操作
    } else toast("没有权限")
  }
初始化时进行判断权限问题

有权限进行其他操作,没有权限申请权限

 if (hasReadExternalStoragePermission() && hasWriteExternalStoragePermission()) {
    } else applyWriteExternalStoragePermission()

lateinit判断是否初始化

lateinit var file: File    

if (::file.isInitialized) { ... }

button去掉背景阴影

AlertDialog设置圆角时候有阴影

window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

获取毫秒时间

Calendar.getInstance().timeInMillis

将map转为json再转换为string

JSONObject(mapOf("half" to half, "a" to a)).toString()
 

设置网络

NetworkStateReceiver.kt

package com.gala.gala.helper

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.widget.Toast
import com.gala.gala.component.AlertDialog
import com.gala.gala.component.AlertDialogWithoutNetwork
import com.gala.gala.stateManager.UserState

class NetworkStateReceiver : BroadcastReceiver() {
  override fun onReceive(context: Context?, intent: Intent?) {
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
      val connMgr = context!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
      val wifiNetworkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
      val dataNetworkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
      if (wifiNetworkInfo.isConnected && dataNetworkInfo.isConnected) {
        Toast.makeText(context, "WIFI连接,移动数据连接", Toast.LENGTH_SHORT).show()
      } else if (!wifiNetworkInfo.isConnected && dataNetworkInfo.isConnected) {
        Toast.makeText(context, "移动数据连接", Toast.LENGTH_SHORT).show()
      } else if (wifiNetworkInfo.isConnected && !dataNetworkInfo.isConnected) {
        Toast.makeText(context, "WIFI连接", Toast.LENGTH_SHORT).show()
      } else {
        Toast.makeText(context, "WIFI断开,移动数据断开", Toast.LENGTH_SHORT).show()
      }
    } else {
      val action = intent!!.action
      if (action == ConnectivityManager.CONNECTIVITY_ACTION) {
        val connMgr = context!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkInfo = connMgr.activeNetworkInfo
        if (networkInfo != null && networkInfo.isAvailable) {
          //数据连接正常
        } else {
          //数据连接异常
      }
    }
  }
}

开启权限

  
  

广播注册

 override fun onResume() {
    super.onResume()
    if (!::networkStateReceiver.isInitialized) {
      networkStateReceiver = NetworkStateReceiver()
    }
    val filter = IntentFilter()
    filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
    registerReceiver(networkStateReceiver, filter)
  }

  override fun onPause() {
    super.onPause()
    unregisterReceiver(networkStateReceiver)
  }

上传文件

参数分别为请求的网址以及本地文件所在的路径

  fun request(url: String, filePath: String): String? {
    val file = File(filePath)
    val mediaType = MediaType.parse("multipart/form-data")
    val requestBody = MultipartBody
        .Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("image", file.name, RequestBody.create(mediaType, file)).build()
    val request = Request
        .Builder()
        .url(url)
    request.put(requestBody)
    val client = OkHttpClient()
    val response = client.newCall(request.build()).execute()
    if (response.code() >= 400) {
      throw error(response.headers())
    }
    return response.body()?.string()
  }

有兴趣可以加入JavaScript交流群,和大佬们一起成长!!!

群号:348108867

kotlin android 踩坑_第4张图片
QQ群

你可能感兴趣的:(kotlin android 踩坑)