美文网首页
kotlin android 踩坑

kotlin android 踩坑

作者: 差很多先生CL | 来源:发表于2018-11-09 16:25 被阅读0次
       Toast.makeText(上下文对象, "这是你要提示的内容", 提示时间).show()
        消息提示       
    
      第一个参数 上下文参数,指当前显示页面,kotlin中使用this@页面名代替java中的    页面名.this
      第二个参数 你需要提示的内容
      第三个参数  显示时间的长短  Toast.LENGTH_SHORT  Toast.LENGTH_LONG
      创建Toast后需要使用show显示出来
    
       Toast.makeText(this@MainActivity, "这是你要提示的内容", Toast.LENGTH_SHORT).show()
    

    示例:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="评分使用"/>
    
        <RatingBar
            android:id="@+id/rbRating"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:layout_editor_absoluteX="0dp"
            tools:layout_editor_absoluteY="51dp" />
    
        <RatingBar
            android:id="@+id/raRating1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="96dp"
            android:layout_marginLeft="96dp"
            android:layout_marginTop="168dp"
            android:isIndicator="false"
            android:max="100"
            android:numStars="4"
            android:rating="2.5"
            android:stepSize="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    
    </android.support.constraint.ConstraintLayout>
    
    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设置圆角

    示例


    圆角边框

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

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <solid android:color="#FFFFFF" />
    
        <stroke
            android:width="0.01dp"
            android:color="#BFBFBF" />
    
        <!-- 这个可以不加,padding用不上
        <padding
            android:bottom="1dp"
            android:left="0.5dp"
            android:right="0.5dp"
            android:top="0dp" /> -->
        <corners
            android:bottomLeftRadius="10dp"
            android:bottomRightRadius="10dp"
            android:topLeftRadius="10dp"
            android:topRightRadius="10dp" />
    
    </shape>
    

    然后在LinearLayout中引入background属性


    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 文件 ,该文件内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
      <base-config cleartextTrafficPermitted="true" />
    </network-security-config>
    

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

    <application
            ...
            android:networkSecurityConfig="@xml/network_security_config"
            ...
    

    全局获取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中

    <manifest ...>
    
     <application
       ...
       android:name="com.XXX.XXX.BaseApplication">
      ...
       <activity android:name=".ui.activity.MainActivity" />
    
     </application>
    
    </manifest>
    

    这样我们就可以用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文件

    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
      <base-config cleartextTrafficPermitted="true" />
    </network-security-config>
    

    导入AndroidManifest.xml

    <application
    ...
     android:networkSecurityConfig="@xml/network_security_config"
    ...
        />
    

    通过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

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
      android:shape="rectangle">
      <size
        android:width="2dp"//光标的宽度
        android:height="8dp" //光标高度
    />
      <solid android:color="@color/topicColor" />
      <padding
      android:bottom="-10dp"
      android:top="-10dp" />
    </shape>
    

    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

    我现在做的结构如下

    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的上拉下拉阴影和右边侧滑栏

      <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:overScrollMode="never"
        android:scrollbars="none">
      </android.support.v7.widget.RecyclerView>
    

    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<out String>, grantResults: IntArray) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    //有权限的操作
        } else toast("没有权限")
      }
    
    初始化时进行判断权限问题

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

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

    lateinit判断是否初始化

    lateinit var file: File    
    
    if (::file.isInitialized) { ... }
    

    button去掉背景阴影

    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/button_send"
    android:onClick="sendMessage"
    style="?android:attr/borderlessButtonStyle" />
    

    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 {
              //数据连接异常
          }
        }
      }
    }
    

    开启权限

      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
      <uses-permission android:name="android.permission.INTERNET"/>
    

    广播注册

     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

    QQ群

    相关文章

      网友评论

          本文标题:kotlin android 踩坑

          本文链接:https://www.haomeiwen.com/subject/exuoxqtx.html