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()

示例:

<?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群
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容