kotlin android 踩坑

   Toast.makeText(上下文對象, "這是你要提示的內(nèi)容", 提示時間).show()
    消息提示       

  第一個參數(shù) 上下文參數(shù),指當(dāng)前顯示頁面,kotlin中使用this@頁面名代替java中的    頁面名.this
  第二個參數(shù) 你需要提示的內(nèi)容
  第三個參數(shù)  顯示時間的長短  Toast.LENGTH_SHORT  Toast.LENGTH_LONG
  創(chuàng)建Toast后需要使用show顯示出來

   Toast.makeText(this@MainActivity, "這是你要提示的內(nèi)容", 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設(shè)置圓角

示例


圓角邊框

在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的實現(xiàn)需要在activity的主線程才能正常工作,所以傳統(tǒng)的非主線程不能使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 限制了明文流量的網(wǎng)絡(luò)請求,非加密的流量請求都會被系統(tǒng)禁止掉

OkHttp3 做了檢查,所以如果使用了明文流量,默認(rèn)情況下,在 Android P 版本 OkHttp3 就拋出異常: CLEARTEXT communication to " + host + " not permitted by network security policy

解決方案:
在 res 下新建一個 xml 目錄,然后創(chuàng)建一個名為:network_security_config.xml 文件 ,該文件內(nèi)容如下:

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

然后在 AndroidManifest.xml application 標(biāo)簽內(nèi)應(yīng)用上面的xml配置:

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

全局獲取context對象

最近做了個需求需要使用到sharePrefrence,但是需要抓到context對象或者activity對象。但是究其原因,我們需要拿到application的context上下文,所以我們?nèi)謱懸粋€繼承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對象然后進(jìn)行sp儲存

val context = GalaApplication.context

問題:在進(jìn)行sp的Set集合存儲的時候發(fā)現(xiàn)存儲進(jìn)去了刷新后讀出來內(nèi)部數(shù)據(jù)仍然為空,

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之后,進(jìn)行更改,然后又put進(jìn)去,這樣是無法更改的

我們在putStringSet的時候new一個新的對象或者我們在進(jìn)行儲存
或者我們在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設(shè)備無法用http非加密明文進(jìn)行網(wǎng)絡(luò)請求,https不受影響。
方案:
1 改用https請求
2 targetSdkVersion 降到27以下
3 更改安全配置
在res創(chuàng)建xml文件夾,創(chuàng)建network_security_config.xml文件

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

導(dǎo)入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資源進(jìn)行setBounds操作,這樣才能夠成功設(shè)置drawer資源

錯誤: okhttp3.internal.http.RealResponseBody@cc75822

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

注意:response.body()在被調(diào)用后,http請求就關(guān)閉了,所以response.body只能被調(diào)用一次。

edittext光標(biāo)高度問題

有時候edittext光標(biāo)的高度大于我們text的輸入高度,如果想要調(diào)小我們需要自定義文件

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"http://光標(biāo)的寬度
    android:height="8dp" //光標(biāo)高度
/>
  <solid android:color="@color/topicColor" />
  <padding
  android:bottom="-10dp"
  android:top="-10dp" />
</shape>

tablayout和viewPage的問題

當(dāng)在加載的時候,viewpage有預(yù)加載的功能,也就是如果有三個tab的話,第一個tab加載的時候會把第二個tab預(yù)加載出來,但是我從第一個tab直接跳到第三個tab會自動刷新數(shù)據(jù),如果我限定數(shù)據(jù)存在時候不再刷新的話,這個時候從第一個頁面跳到第三個會沒有數(shù)據(jù)顯示。

方法

viewPage.offscreenPageLimit = 3

限制三個頁面一起預(yù)加載。再進(jìn)行是否渲染數(shù)據(jù)的判斷

如果你想取消預(yù)更新,可以參考這兩篇文章

https://blog.csdn.net/chenzheng8975/article/details/54645704
http://www.itdecent.cn/p/66ff0330f2d9
http://www.itdecent.cn/p/eb81f3692229

錯誤Error parsing XML: not well-formed (invalid token)

解決辦法:找到對應(yīng)報錯的XML,然后看看自己哪里寫錯了多手了!??!

更改LinearLayout的寬度(當(dāng)高寬是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

我現(xiàn)在做的結(jié)構(gòu)如下

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出現(xiàn)了數(shù)據(jù)錯亂的問題。

使用recycleview填充數(shù)據(jù)的時候,在滑動過程中發(fā)生數(shù)據(jù)重疊的問題。原因是recycleview存在復(fù)用的問題,參考http://www.itdecent.cn/p/697ce543b1c1
解決 在設(shè)置adapter的時候加入adapter.setHasStableIds(true),用于回收復(fù)用機(jī)制中,給 ViewHoler 設(shè)置一個唯一的標(biāo)識符
adapter.setHasStableIds(true)
chat_ai_recycle_view.adapter = adapter

問題:RecycleView滑動的時候出現(xiàn)了數(shù)據(jù)錯亂的問題。上滑下滑前后數(shù)據(jù)不同

解決:由于RecyclerView的onBindViewHolder()方法,只有在getItemViewType()返回類型不同時才會調(diào)用,所以我們?yōu)榱俗屗看握{(diào)用onBindViewHolder()方法,我們需要重寫getItemViewType()并將每次的position return回去
 override fun getItemViewType(position: Int): Int {
    return position
  }
在viewHolder中動態(tài)添加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

問題: 當(dāng)我們加入一條數(shù)據(jù)后,希望繼續(xù)固定在底部

在我們觸發(fā)發(fā)送后端處理的時候設(shè)置recycleView的scrollToPosition方法
edit_text.setOnEditorActionListener{ _, actionId, _ ->
  if (actionId == EditorInfo.IME_ACTION_SEND) {
      //隱藏鍵盤
      hideSoftKeyBoard()
      //后端在這里處理數(shù)據(jù)
      chat_ai_recycle_view.scrollToPosition(presenter.messageList.size - 1)
      chat_ai_edit_text.text = null
  }
  true
}

問題:EditText多行和監(jiān)聽鍵盤事件

描述:今天碰到EditText的問題。當(dāng)inputType為textMultiLine的時候,可以換行但是發(fā)現(xiàn)監(jiān)聽更改鍵盤事件無效。inputType為text的時候卻是單行。

原因:在inputType = textMultiLine 時,點擊回車鍵的默認(rèn)操作就是換行,不會有其他的事件觸發(fā)
解決:inputType為text,在代碼中添加
chat_ai_edit_text.maxLines = 3
chat_ai_edit_text.setHorizontallyScrolling(false)
監(jiān)聽鍵盤發(fā)送事件
chat_ai_edit_text.setOnEditorActionListener { _, actionId, _ ->
      if (actionId == EditorInfo.IME_ACTION_SEND) {
        hideSoftKeyBoard()
        //在這里進(jìn)行數(shù)據(jù)處理
      chat_ai_recycle_view.scrollToPosition(presenter.messageList.size - 1)
        chat_ai_edit_text.text = null
      }
      false
    }

問題:想在fragment中調(diào)用activity的presenter對象。使得數(shù)據(jù)保持一致。

思路:在fragment中寫一個函數(shù),在activity中調(diào)用并將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的上拉下拉陰影和右邊側(cè)滑欄

  <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設(shè)置uri圖片

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

獲取動態(tài)權(quán)限

  //動態(tài)申請權(quán)限
  private fun applyWriteExternalStoragePermission() {
    val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
    ActivityCompat.requestPermissions(this, permissions, 0)
  }

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

//READ_EXTERNAL_STORAGE權(quán)限
  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) {
//有權(quán)限的操作
    } else toast("沒有權(quán)限")
  }
初始化時進(jìn)行判斷權(quán)限問題

有權(quán)限進(jìn)行其他操作,沒有權(quán)限申請權(quán)限

 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設(shè)置圓角時候有陰影

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

獲取毫秒時間

Calendar.getInstance().timeInMillis

將map轉(zhuǎn)為json再轉(zhuǎn)換為string

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

設(shè)置網(wǎng)絡(luò)

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連接,移動數(shù)據(jù)連接", Toast.LENGTH_SHORT).show()
      } else if (!wifiNetworkInfo.isConnected && dataNetworkInfo.isConnected) {
        Toast.makeText(context, "移動數(shù)據(jù)連接", Toast.LENGTH_SHORT).show()
      } else if (wifiNetworkInfo.isConnected && !dataNetworkInfo.isConnected) {
        Toast.makeText(context, "WIFI連接", Toast.LENGTH_SHORT).show()
      } else {
        Toast.makeText(context, "WIFI斷開,移動數(shù)據(jù)斷開", 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) {
          //數(shù)據(jù)連接正常
        } else {
          //數(shù)據(jù)連接異常
      }
    }
  }
}

開啟權(quán)限

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

上傳文件

參數(shù)分別為請求的網(wǎng)址以及本地文件所在的路徑

  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群
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容