Android学习体会

布局

button,textview,edittext,view,radiobutton,checkbox,imageview,listview,gridview,recyclerview,scrollview,webview等控件

重写方法 toast,dialog,alertdialog,progressbar,fragment,activity,popupwindow activity

event 监听,回调

handler, broadcast,动画animate

存储,File内部存储,外部存储

通过getItemOffsets添加间隔线,是让每个Item分开,然后露出background,”间隔线“就是background的颜色

class MyDecoration extends RecyclerView.ItemDecoration
    {
        @Override
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)
        {
            super.getItemOffsets(outRect, view, parent, state);
            outRect.set(0, 0, 0, getResources().getDimensionPixelOffset(R.dimen.dividerHeight));
        }
    }

Fragment

按回退键,实例是之前的实例,但是会重新创建视图

监听

通过内部类
通过匿名内部类
通过事件源所在的类:在所在类中,加一个onclick的方法
通过外部类:在新建一个外部类
在xml,添加一个onclick的标签,在java里添加有onclick标签的方法

在这里插入图片描述

联网

String url= “https://andfun-weather.udacity.com/staticweather?q=94043%2CUSA&mode=json&units=metric&cnt=14”;
try {
URL per = new URL(url);
HttpURLConnection urlConnection2 = (HttpURLConnection) per.openConnection();
Log.d(“HttpURLConnection2”,urlConnection2.toString());
InputStream in= urlConnection2.getInputStream();
BufferedReader buf = new BufferedReader(new InputStreamReader(in));
while(true) {
String ex = buf.readLine();
// buf.r;
Log.d(“exampel”,ex);
if(ex.length()0||exnull){
break;
}
}

json

处理json string
String:forecastJsonStr
JSONObject forecastJson = new JSONObject(forecastJsonStr);
forecastJson.has(OWM_MESSAGE_CODE) 查看json是否有某个标签
int errorCode = forecastJson.getInt(OWM_MESSAGE_CODE); 这个标签下是int,可以通过getInt获取int
JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);
OWM_LIST标签下是JsonArray
for(int i=0;i<weatherArray.length();i++){
Log.d(“JSONArray”, weatherArray.getJSONObject(i).toString());
}
获取每个Array的object,用getJsonObject(i)

AsyncTask

<li>{@link #onPreExecute()}, invoked on the UI thread before the task
 *     is executed. This step is normally used to setup the task, for instance by
 *     showing a progress bar in the user interface.</li>
 * 
 *     <li>{@link #doInBackground}, invoked on the background thread
 *     immediately after {@link #onPreExecute()} finishes executing. This step is used
 *     to perform background computation that can take a long time. The parameters
 *     of the asynchronous task are passed to this step. The result of the computation must
 *     be returned by this step and will be passed back to the last step. This step
 *     can also use {@link #publishProgress} to publish one or more units
 *     of progress. These values are published on the UI thread, in the
 *     {@link #onProgressUpdate} step.</li>
 * 
 *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
 *     call to {@link #publishProgress}. The timing of the execution is
 *     undefined. This method is used to display any form of progress in the user
 *     interface while the background computation is still executing. For instance,
 *     it can be used to animate a progress bar or show logs in a text field.</li>
 * 
 *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
 *     computation finishes. The result of the background computation is passed to
 *     this step as a parameter.</li>

recyclerview

假设你的 RecyclerView 列表中的每项包含四个数据视图,你没有在 ViewHolder 中缓存这些视图。如果 8 项内容可以占满屏幕,那么要滚动查看 30 项内容,大概需要额外调用 findViewById() 多少次?

除了占满屏幕的 8 项内容外,假设再需要 2 项内容来实现流程滚动效果。

answer

如果屏幕上只有 8 项内容,我们需要至少 8 个项目视图,但是问题提到我们再需要 2 项内容来实现流程滚动效果,也就是共 10 项。10 项内容乘以 4 个数据视图等于 40 次 FindViewById 调用。我们可以将这些视图缓存在 ViewHolder 中,以填充我们的 RecyclerView,然后在滚动和回收视图时访问它们。如果没有使用 ViewHolder,那么滚动浏览的每个项目需要调用 FindViewById 4 次,一共有 30 个项目,所以需要调用 FindViewById 30x4(即 120)次。

所以使用和不使用 ViewHolder 的区别是分别调用 FindViewById 120 次和 40 次。不使用 ViewHolder 的话要多调用 80 次!如今的 Android 手机速度非常快,你可能不会注意到这一优化,但是会稍微延长电池电量寿命,如果滚动浏览非常庞大的列表,就会比较明显了。所以最好使用 ViewHolder。

implementation ‘com.android.support:recyclerview-v7:25.1.0’
mNumbersList = (RecyclerView) findViewById(R.id.rv_numbers);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mNumbersList.setLayoutManager(layoutManager);
mNumbersList.setHasFixedSize(true);
mAdapter = new GreenAdapter(NUM_LIST_ITEMS);
mNumbersList.setAdapter(mAdapter);

tips

// TODO (18) Be careful to get the String representation of listIndex, as using setText with an int does something different

public void bind(int listIndex){
listItemNumberView.setText(String.valueOf(listIndex) );

    }

不要
listItemNumberView.setText((listIndex) );

给recyclerview上颜色

可以通过view的setBackgroundColor,给定一个颜色的编号,就可以上颜色
通过这个,可以找到颜色的编号:context的getColor

public static int getViewHolderBackgroundColorFromInstance(Context context, int instanceNum) {
switch (instanceNum) {
case 0:
return ContextCompat.getColor(context, R.color.material50Green);
case 1:
return ContextCompat.getColor(context, R.color.material100Green);

    }
public NumberViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        Context context = viewGroup.getContext();
        int layoutIdForListItem = R.layout.number_list_item;
        LayoutInflater inflater = LayoutInflater.from(context);
        boolean shouldAttachToParentImmediately = false;

        View view = inflater.inflate(layoutIdForListItem, viewGroup, shouldAttachToParentImmediately);
        NumberViewHolder viewHolder = new NumberViewHolder(view);

        // COMPLETED (12) Set the text of viewHolderIndex to "ViewHolder index: " + viewHolderCount
        viewHolder.viewHolderIndex.setText("ViewHolder index: " + viewHolderCount);

        // COMPLETED (13) Use ColorUtils.getViewHolderBackgroundColorFromInstance and pass in a Context and the viewHolderCount
        int backgroundColorForViewHolder = ColorUtils
                .getViewHolderBackgroundColorFromInstance(context, viewHolderCount);
        Log.d("color",String.valueOf(backgroundColorForViewHolder));
        // COMPLETED (14) Set the background color of viewHolder.itemView with the color from above
//        viewHolder.itemView.setBackgroundColor(backgroundColorForViewHolder);
        
        viewHolder.itemView.setBackgroundColor(-3610935);
        // COMPLETED (15) Increment viewHolderCount and log its value
        viewHolderCount++;
        Log.d(TAG, "onCreateViewHolder: number of ViewHolders created: "
                + viewHolderCount);
        return viewHolder;
    }

给adapter加监听事件

// TODO (1) Add an interface called ListItemClickListener

public interface ListItemClickListener{
    void onListItemClick(int num);
}
// TODO (2) Within that interface, define a void method called onListItemClick that takes an int as a parameter
// TODO (3) Create a final private ListItemClickListener called mOnClickListener
private ListItemClickListener mOnClickListener;
// TODO (4) Add a ListItemClickListener as a parameter to the constructor and store it in mOnClickListener
// TODO (5) Implement OnClickListener in the NumberViewHolder class

/**
 * Cache of the children views for a list item.
 */
class NumberViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// TODO (6) Override onClick, passing the clicked item's position (getAdapterPosition())
    //  to mOnClickListener via its onListItemClick method
}
@Override
    public void onClick(View view) {
        int clickedPosition = getAdapterPosition();
        mOnClickListener.onListItemClick(clickedPosition);
    }
    // TODO (7) Call setOnClickListener on the View passed into the constructor (use 'this' as the OnClickListener)
    
public NumberViewHolder(View itemView) {
        super(itemView);
        listItemNumberView = (TextView) itemView.findViewById(R.id.tv_item_number);
        viewHolderIndex = (TextView) itemView.findViewById(R.id.tv_view_holder_instance);
        
        itemView.setOnClickListener(this);
    }
    // TODO (8) Implement GreenAdapter.ListItemClickListener from the MainActivity
public class MainActivity extends AppCompatActivity implements GreenAdapter.ListItemClickListener{
// TODO (9) Create a Toast variable called mToast to store the current Toast
Toast mToast;
// TODO (10) Override ListItemClickListener's onListItemClick method
// TODO (11) In the beginning of the method, cancel the Toast if it isn't null
// TODO (12) Show a Toast when an item is clicked, displaying that item number that was clicked
@Override
public void onListItemClick(int num) {
    if(mToast==null){
        mToast.cancel();
    }
}

// TODO (13) Pass in this as the ListItemClickListener to the GreenAdapter constructor

 mAdapter = new GreenAdapter(NUM_LIST_ITEMS, MainActivity.this);
 
  // TODO (14) Pass in this as the ListItemClickListener to the GreenAdapter constructor
  
            mAdapter = new GreenAdapter(NUM_LIST_ITEMS, this);

打开网页链接是隐式 Intent,因为你没有指定要具体使用哪个浏览器,而是由用户来选择。

打开 Activity 使用的是显式 Intent,因为你明确知道要转到何处。

向 Twitter 分享内容则比较模糊。我们讲授了最佳方法,即使用隐式 Intent。也可以使用显式 Intent,但是不推荐

openMap

        String addressString = "1600 Ampitheatre Parkway, CA";
        Uri geoLocation = Uri.parse("geo:0,0?q=" + addressString);

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(geoLocation);

        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        } else {
            Log.d(TAG, "Couldn't call " + geoLocation.toString()
                    + ", no receiving apps installed!");
        }
    }

shareText

private void shareText(String textToShare) {
        // COMPLETED (2) Create a String variable called mimeType and set it to "text/plain"
        /*
         * You can think of MIME types similarly to file extensions. They aren't the exact same,
         * but MIME types help a computer determine which applications can open which content. For
         * example, if you double click on a .pdf file, you will be presented with a list of
         * programs that can open PDFs. Specifying the MIME type as text/plain has a similar affect
         * on our implicit Intent. With text/plain specified, all apps that can handle text content
         * in some way will be offered when we call startActivity on this particular Intent.
         */
        String mimeType = "text/plain";

        // COMPLETED (3) Create a title for the chooser window that will pop up
        /* This is just the title of the window that will pop up when we call startActivity */
        String title = "Learning How to Share";

        // COMPLETED (4) Use ShareCompat.IntentBuilder to build the Intent and start the chooser
        /* ShareCompat.IntentBuilder provides a fluent API for creating Intents */
        ShareCompat.IntentBuilder
                /* The from method specifies the Context from which this share is coming from */
                .from(this)
                .setType(mimeType)
                .setChooserTitle(title)
                .setText(textToShare)
                .startChooser();
    }

附加Menu

private Intent createShareForecastIntent() {
        Intent shareIntent = ShareCompat.IntentBuilder.from(this)
                .setType("text/plain")
                .setText(mForecast + FORECAST_SHARE_HASHTAG)
                .getIntent();
        return shareIntent;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.detail, menu);
        MenuItem menuItem = menu.findItem(R.id.action_share);
        menuItem.setIntent(createShareForecastIntent());
        return true;
    }

Activity lifecycle

很棒!旋转设备后导致 Activity 被销毁并重新创建,所以我们的生命周期从 onPause 开始并结束于 onResume。注意,我们没有看到 onRestart,只有 Activity 被停止(但是没有销毁)然后重新开始时才会发生。

storage

你的笔记应用中存在 bug。有时候,当你正在输入笔记内容,然后离开应用,再回到应用时,消息丢失了。你会使用下面的哪个数据持久性方法来解决此 bug。你问用户他们是否希望关机或关闭应用后,将写到一半的笔记保存了,他们说不希望。

在这种情况下,你至少应该将笔记保存在 onSavedInstanceState 中,这样可以防止如果用户不再查看该应用或系统关闭了应用后,写到一半的笔记不会被删除。如果你想在应用重新启动期间保存笔记,则可以使用 SharedPreferences 或 SQLite。

onSavedInstanceState 不会在应用或手机重新启动期间保存数据,不适合保存设置。如果使用此选项,则每次关闭应用后都需要重新设置。

顾名思义,SharedPreferences(通常)是保存用户偏好设置的最佳选择。因为偏好设置通常是你希望在应用重新启动期间保存的几个值。SharedPreferences 是保存用户设置的不错选择还有其他原因,我们将了解更多,敬请期待

SharedPreferences

private void setupSharedPreferences() {
        // Get all of the values from shared preferences to set it up
        // COMPLETED (2) Get a reference to the default shared preferences from the PreferenceManager class
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        // COMPLETED (3) Get the value of the show_bass checkbox preference and use it to call setShowBass
        mVisualizerView.setShowBass(sharedPreferences.getBoolean("show_bass", true));
        mVisualizerView.setShowMid(true);
        mVisualizerView.setShowTreble(true);
        mVisualizerView.setMinSizeScale(1);
        mVisualizerView.setColor(getString(R.string.pref_color_red_value));
    }

setShowBass的属性设置,从sharedPreference中设置
SharedPreference sharedPreference= PreferenceManager.getDefaultSharedPreferences(this)
sharedPreferences.getBoolean(“show_bass”, true)
但是setupSharedPreferences方法是在onCreate中调用,只有旋转屏幕,重新onCreate后,才能生效
是的。onCreate 代码包含加载偏好设置的代码,但是当你从 Activity 返回时,onCreate 没有被调用。Activity 只是被暂停,没有被销毁,所以 onResume 和 onStart 被调用,未调用其他项了

if sharedpreference 超出范围

设置可接受的范围
要将可接受的值限制在 0(不包括)和 3(包括)之间,我们选择使用 PreferenceChangeListener - 它与 SharedPreferenceChangeListener 的不同之处为:

SharedPreferenceChangeListener 在任何值保存到 SharedPreferences 文件后被触发。
PreferenceChangeListener 在值保存到 SharedPreferences 文件前被触发。因此,可以防止对偏好设置做出无效的更新。PreferenceChangeListeners 也附加到了单个偏好设置上。
流程通常如下所示:

用户更新偏好设置。
该偏好设置触发了 PreferenceChangeListener。
新的值保存到了 SharedPreference 文件。
onSharedPreferenceChanged 监听器被触发。
除此之外,它们的行为很相似。你将在你的 Activity 中实现 Preference.OnPreferenceChangeListener,覆盖 onPreferenceChange(Preference preference, Object newValue)。 onPreferenceChange 方法将返回 true 或 false,取决于偏好设置实际上是否要被保存。

livedata

大部分Android应用会从网络或SQLite数据库存取数据,并根据数据更新界面。为了避免ANR,主线程中不能存取数据。而后台线程中无法更新界面。通常的做法是让后台线程将数据通过Handler传递给主线程。但是当界面较为复杂时,使用Handler这种方式变得非常复杂。为了简化界面更新的工作,Google在Jetpack中提供了LiveData组件。LiveData具有保证UI和状态一致、避免内存泄漏、避免手动管理生命周期等优点。

每次onresume调用时,都会通过retrieveTasks查询数据库,更新数据,不管数据是否更新,这是很低效的,因此我们使用livedata,去掉onResume重载,仅在observe到变化时,查询数据库)

runOnUiThread()

final Handler mHandler = new Handler();
private Thread mUiThread;
// …
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
// …
}
首先在主线程里通过无参的构造方法创建一个Handler,这个Handler是指向主线程的。
当执行runOnUiThread()时,当前线程不是主线程,调用mHandler.post(action),将Runnable添加到主线程的消息队列中
这样,Runnable的语句就是在主线程执行的了。

viewModel

thans to liveData, we are notified of changes of our database without needing to be continuously re-querying. Unfortunately, we are still querying our database every time we rotate our device. As you know, activites are destroyed and recreated on rotation. Once the onCreate method is used, a new live data object will be created and there will be a new call to the database.
The usual approach of using OnSavedInstanceState is meant for a small amounts of data that can be easily serialized and deserialized.
Here comes the ViewModel. ViewModel allows data to survive to configuration changes such as rotation.

Without the ViewModel, if the activity is destroyed before the call finishes, it will cause the memory leak. If we use ViewModel, we can make this asynchronous calls from the ViewModel, the result will be delivered back to the ViewModel. Then it does not matter if the activity has been destroyed or not, then there will be no memory leak problems.

fragment

Fragment
FragmentManager fragmentManager = getSupportManager();
fragmentManager.beginTransaction().add(R.id.,headfragment).commit();

在fragment内,有方法直接getContext()找到context

指定fragment两种方法:
1、用class: 在元素中的android:name属性指定了在布局中要实例化的Fragment class。在class中有onCreateView,里面inflate了fragment的xml
2、用xml: 使用FragmentManager.beginTransaction().add(R.id.要添加的fragment xml id,实例的fragment).commit();

ImageView

imageView.setImageResource(R.id)
或者是一个包装好的R.id.

override onSaveInstanceState

buddle.putInt
buddle.putIntegerArrayList
在mainActivity中加if(savedInstanceState == null){
再create
否则就保持原有加载不变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值