Android 天气APP(十三)仿微信弹窗(右上角加号点击弹窗效果)、自定义背景图片、UI优化调整

简介: Android 天气APP(十三)仿微信弹窗(右上角加号点击弹窗效果)、自定义背景图片、UI优化调整

前言


自定义背景,做这个功能的原因是因为一些人觉得必应的每日一图并不好看,想要手动上传自己手机里的壁纸作为背景,并且应用也要有自带的壁纸供用户选择。正所谓有需就有求,这是亘古不变的道理,第三个就是UI的优化,这次我是打算把切换城市的弹窗挪到二级菜单里面,右上角做一个一级菜单列表,这个列表暂定功能为切换城市和切换背景,这样做也是符合大众APP的审美,比如微信、支付宝、QQ之类的。


正文


不知道你们是不是被这个仿微信弹窗的内容吸引进来的,是的话,你就来对地方了。我不是标题党,我是踏踏实实的程序员,不搞那些花里胡哨的文章标题,实事求是,我觉那种标题吸引别人进来看,结果什么有用的知识都没有,得浪费别人的时间等同于诈骗。


弹窗


首先在项目的layout布局文件中创建一个弹窗的布局

window_add.xml,弹窗的背景图片是一个.9可拉伸的png图片,


20200508160101238.png

20200508160101229.png


一黑一白,想用自取即可,或者可以去GitHub上面源码里去取图。

在上一篇文章中,在mvplibrary中的res文件下新建了colors.xml,并在里面新增几个颜色进去,所以为了更好的管理项目中的颜色,后续的颜色都会写在这里,其他页面通过@color/black来调用即可,

20200508160841579.png


然后再创建一个dimen.xml文件,这个里面主要防止尺寸大小和字体大小

20200508161008994.png


调用方式通过@dimen/dp_10或者@dimen/sp_10

因为下面的布局文件中会涉及到这两个xml里面的内容,所以我这里会说的比较清楚。

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="arc_bg_color">#C6D7F4</color>
    <color name="arc_progress_color">#FBFEF7</color>
    <color name="white">#ffffff</color><!--白色-->
    <color name="black">#000000</color><!--黑色-->
    <color name="blue_one">#9FC8E9</color><!--浅蓝色-->
    <color name="transparent">#00000000</color><!--透明-->
    <color name="transparent_bg">#22000000</color><!--半透明-->
</resources>


dimen.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--尺寸-->
    <dimen name="dp_0">0dp</dimen>
    <dimen name="dp_0_1">0.1dp</dimen>
    <dimen name="dp_0_5">0.5dp</dimen>
    <dimen name="dp_1">1dp</dimen>
    <dimen name="dp_1_5">1.5dp</dimen>
    <dimen name="dp_2">2dp</dimen>
    <dimen name="dp_2_5">2.5dp</dimen>
    <dimen name="dp_3">3dp</dimen>
    <dimen name="dp_3_5">3.5dp</dimen>
    <dimen name="dp_4">4dp</dimen>
    <dimen name="dp_4_5">4.5dp</dimen>
    <dimen name="dp_5">5dp</dimen>
    <dimen name="dp_6">6dp</dimen>
    <dimen name="dp_7">7dp</dimen>
    <dimen name="dp_8">8dp</dimen>
    <dimen name="dp_9">9dp</dimen>
    <dimen name="dp_10">10dp</dimen>
    <dimen name="dp_11">11dp</dimen>
    <dimen name="dp_12">12dp</dimen>
    <dimen name="dp_13">13dp</dimen>
    <dimen name="dp_14">14dp</dimen>
    <dimen name="dp_15">15dp</dimen>
    <dimen name="dp_16">16dp</dimen>
    <dimen name="dp_17">17dp</dimen>
    <dimen name="dp_18">18dp</dimen>
    <dimen name="dp_19">19dp</dimen>
    <dimen name="dp_20">20dp</dimen>
    <dimen name="dp_21">21dp</dimen>
    <dimen name="dp_22">22dp</dimen>
    <dimen name="dp_23">23dp</dimen>
    <dimen name="dp_24">24dp</dimen>
    <dimen name="dp_25">25dp</dimen>
    <dimen name="dp_26">26dp</dimen>
    <dimen name="dp_27">27dp</dimen>
    <dimen name="dp_28">28dp</dimen>
    <dimen name="dp_29">29dp</dimen>
    <dimen name="dp_30">30dp</dimen>
    <dimen name="dp_31">31dp</dimen>
    <dimen name="dp_32">32dp</dimen>
    <dimen name="dp_33">33dp</dimen>
    <dimen name="dp_34">34dp</dimen>
    <dimen name="dp_35">35dp</dimen>
    <dimen name="dp_36">36dp</dimen>
    <dimen name="dp_37">37dp</dimen>
    <dimen name="dp_38">38dp</dimen>
    <dimen name="dp_39">39dp</dimen>
    <dimen name="dp_40">40dp</dimen>
    <dimen name="dp_41">41dp</dimen>
    <dimen name="dp_42">42dp</dimen>
    <dimen name="dp_43">43dp</dimen>
    <dimen name="dp_44">44dp</dimen>
    <dimen name="dp_45">45dp</dimen>
    <dimen name="dp_46">46dp</dimen>
    <dimen name="dp_47">47dp</dimen>
    <dimen name="dp_48">48dp</dimen>
    <dimen name="dp_49">49dp</dimen>
    <dimen name="dp_50">50dp</dimen>
    <dimen name="dp_51">51dp</dimen>
    <dimen name="dp_52">52dp</dimen>
    <dimen name="dp_53">53dp</dimen>
    <dimen name="dp_54">54dp</dimen>
    <dimen name="dp_55">55dp</dimen>
    <dimen name="dp_56">56dp</dimen>
    <dimen name="dp_57">57dp</dimen>
    <dimen name="dp_58">58dp</dimen>
    <dimen name="dp_59">59dp</dimen>
    <dimen name="dp_60">60dp</dimen>
    <dimen name="dp_61">61dp</dimen>
    <dimen name="dp_62">62dp</dimen>
    <dimen name="dp_63">63dp</dimen>
    <dimen name="dp_64">64dp</dimen>
    <dimen name="dp_65">65dp</dimen>
    <dimen name="dp_66">66dp</dimen>
    <dimen name="dp_67">67dp</dimen>
    <dimen name="dp_68">68dp</dimen>
    <dimen name="dp_69">69dp</dimen>
    <dimen name="dp_70">70dp</dimen>
    <dimen name="dp_71">71dp</dimen>
    <dimen name="dp_72">72dp</dimen>
    <dimen name="dp_73">73dp</dimen>
    <dimen name="dp_74">74dp</dimen>
    <dimen name="dp_75">75dp</dimen>
    <dimen name="dp_76">76dp</dimen>
    <dimen name="dp_77">77dp</dimen>
    <dimen name="dp_78">78dp</dimen>
    <dimen name="dp_79">79dp</dimen>
    <dimen name="dp_80">80dp</dimen>
    <dimen name="dp_81">81dp</dimen>
    <dimen name="dp_82">82dp</dimen>
    <dimen name="dp_83">83dp</dimen>
    <dimen name="dp_84">84dp</dimen>
    <dimen name="dp_85">85dp</dimen>
    <dimen name="dp_86">86dp</dimen>
    <dimen name="dp_87">87dp</dimen>
    <dimen name="dp_88">88dp</dimen>
    <dimen name="dp_89">89dp</dimen>
    <dimen name="dp_90">90dp</dimen>
    <dimen name="dp_91">91dp</dimen>
    <dimen name="dp_92">92dp</dimen>
    <dimen name="dp_93">93dp</dimen>
    <dimen name="dp_94">94dp</dimen>
    <dimen name="dp_95">95dp</dimen>
    <dimen name="dp_96">96dp</dimen>
    <dimen name="dp_97">97dp</dimen>
    <dimen name="dp_98">98dp</dimen>
    <dimen name="dp_99">99dp</dimen>
    <dimen name="dp_100">100dp</dimen>
    <dimen name="dp_101">101dp</dimen>
    <dimen name="dp_102">102dp</dimen>
    <dimen name="dp_103">103dp</dimen>
    <dimen name="dp_104">104dp</dimen>
    <dimen name="dp_105">105dp</dimen>
    <dimen name="dp_106">106dp</dimen>
    <dimen name="dp_107">107dp</dimen>
    <dimen name="dp_108">108dp</dimen>
    <dimen name="dp_109">109dp</dimen>
    <dimen name="dp_110">110dp</dimen>
    <dimen name="dp_111">111dp</dimen>
    <dimen name="dp_112">112dp</dimen>
    <dimen name="dp_113">113dp</dimen>
    <dimen name="dp_114">114dp</dimen>
    <dimen name="dp_115">115dp</dimen>
    <dimen name="dp_116">116dp</dimen>
    <dimen name="dp_117">117dp</dimen>
    <dimen name="dp_118">118dp</dimen>
    <dimen name="dp_119">119dp</dimen>
    <dimen name="dp_120">120dp</dimen>
    <dimen name="dp_121">121dp</dimen>
    <dimen name="dp_122">122dp</dimen>
    <dimen name="dp_123">123dp</dimen>
    <dimen name="dp_124">124dp</dimen>
    <dimen name="dp_125">125dp</dimen>
    <dimen name="dp_126">126dp</dimen>
    <dimen name="dp_127">127dp</dimen>
    <dimen name="dp_128">128dp</dimen>
    <dimen name="dp_129">129dp</dimen>
    <dimen name="dp_130">130dp</dimen>
    <dimen name="dp_131">131dp</dimen>
    <dimen name="dp_132">132dp</dimen>
    <dimen name="dp_133">133dp</dimen>
    <dimen name="dp_134">134dp</dimen>
    <dimen name="dp_135">135dp</dimen>
    <dimen name="dp_136">136dp</dimen>
    <dimen name="dp_137">137dp</dimen>
    <dimen name="dp_138">138dp</dimen>
    <dimen name="dp_139">139dp</dimen>
    <dimen name="dp_140">140dp</dimen>
    <dimen name="dp_141">141dp</dimen>
    <dimen name="dp_142">142dp</dimen>
    <dimen name="dp_143">143dp</dimen>
    <dimen name="dp_144">144dp</dimen>
    <dimen name="dp_145">145dp</dimen>
    <dimen name="dp_146">146dp</dimen>
    <dimen name="dp_147">147dp</dimen>
    <dimen name="dp_148">148dp</dimen>
    <dimen name="dp_149">149dp</dimen>
    <dimen name="dp_150">150dp</dimen>
    <dimen name="dp_151">151dp</dimen>
    <dimen name="dp_152">152dp</dimen>
    <dimen name="dp_153">153dp</dimen>
    <dimen name="dp_154">154dp</dimen>
    <dimen name="dp_155">155dp</dimen>
    <dimen name="dp_156">156dp</dimen>
    <dimen name="dp_157">157dp</dimen>
    <dimen name="dp_158">158dp</dimen>
    <dimen name="dp_159">159dp</dimen>
    <dimen name="dp_160">160dp</dimen>
    <dimen name="dp_161">161dp</dimen>
    <dimen name="dp_162">162dp</dimen>
    <dimen name="dp_163">163dp</dimen>
    <dimen name="dp_164">164dp</dimen>
    <dimen name="dp_165">165dp</dimen>
    <dimen name="dp_166">166dp</dimen>
    <dimen name="dp_167">167dp</dimen>
    <dimen name="dp_168">168dp</dimen>
    <dimen name="dp_169">169dp</dimen>
    <dimen name="dp_170">170dp</dimen>
    <dimen name="dp_171">171dp</dimen>
    <dimen name="dp_172">172dp</dimen>
    <dimen name="dp_173">173dp</dimen>
    <dimen name="dp_174">174dp</dimen>
    <dimen name="dp_175">175dp</dimen>
    <dimen name="dp_176">176dp</dimen>
    <dimen name="dp_177">177dp</dimen>
    <dimen name="dp_178">178dp</dimen>
    <dimen name="dp_179">179dp</dimen>
    <dimen name="dp_180">180dp</dimen>
    <dimen name="dp_181">181dp</dimen>
    <dimen name="dp_182">182dp</dimen>
    <dimen name="dp_183">183dp</dimen>
    <dimen name="dp_184">184dp</dimen>
    <dimen name="dp_185">185dp</dimen>
    <dimen name="dp_186">186dp</dimen>
    <dimen name="dp_187">187dp</dimen>
    <dimen name="dp_188">188dp</dimen>
    <dimen name="dp_189">189dp</dimen>
    <dimen name="dp_190">190dp</dimen>
    <dimen name="dp_191">191dp</dimen>
    <dimen name="dp_192">192dp</dimen>
    <dimen name="dp_193">193dp</dimen>
    <dimen name="dp_194">194dp</dimen>
    <dimen name="dp_195">195dp</dimen>
    <dimen name="dp_196">196dp</dimen>
    <dimen name="dp_197">197dp</dimen>
    <dimen name="dp_198">198dp</dimen>
    <dimen name="dp_199">199dp</dimen>
    <dimen name="dp_200">200dp</dimen>
    <dimen name="dp_201">201dp</dimen>
    <dimen name="dp_202">202dp</dimen>
    <dimen name="dp_203">203dp</dimen>
    <dimen name="dp_204">204dp</dimen>
    <dimen name="dp_205">205dp</dimen>
    <dimen name="dp_206">206dp</dimen>
    <dimen name="dp_207">207dp</dimen>
    <dimen name="dp_208">208dp</dimen>
    <dimen name="dp_209">209dp</dimen>
    <dimen name="dp_210">210dp</dimen>
    <dimen name="dp_211">211dp</dimen>
    <dimen name="dp_212">212dp</dimen>
    <dimen name="dp_213">213dp</dimen>
    <dimen name="dp_214">214dp</dimen>
    <dimen name="dp_215">215dp</dimen>
    <dimen name="dp_216">216dp</dimen>
    <dimen name="dp_217">217dp</dimen>
    <dimen name="dp_218">218dp</dimen>
    <dimen name="dp_219">219dp</dimen>
    <dimen name="dp_220">220dp</dimen>
    <dimen name="dp_221">221dp</dimen>
    <dimen name="dp_222">222dp</dimen>
    <dimen name="dp_223">223dp</dimen>
    <dimen name="dp_224">224dp</dimen>
    <dimen name="dp_225">225dp</dimen>
    <dimen name="dp_226">226dp</dimen>
    <dimen name="dp_227">227dp</dimen>
    <dimen name="dp_228">228dp</dimen>
    <dimen name="dp_229">229dp</dimen>
    <dimen name="dp_230">230dp</dimen>
    <dimen name="dp_231">231dp</dimen>
    <dimen name="dp_232">232dp</dimen>
    <dimen name="dp_233">233dp</dimen>
    <dimen name="dp_234">234dp</dimen>
    <dimen name="dp_235">235dp</dimen>
    <dimen name="dp_236">236dp</dimen>
    <dimen name="dp_237">237dp</dimen>
    <dimen name="dp_238">238dp</dimen>
    <dimen name="dp_239">239dp</dimen>
    <dimen name="dp_240">240dp</dimen>
    <dimen name="dp_241">241dp</dimen>
    <dimen name="dp_242">242dp</dimen>
    <dimen name="dp_243">243dp</dimen>
    <dimen name="dp_244">244dp</dimen>
    <dimen name="dp_245">245dp</dimen>
    <dimen name="dp_246">246dp</dimen>
    <dimen name="dp_247">247dp</dimen>
    <dimen name="dp_248">248dp</dimen>
    <dimen name="dp_249">249dp</dimen>
    <dimen name="dp_250">250dp</dimen>
    <dimen name="dp_251">251dp</dimen>
    <dimen name="dp_252">252dp</dimen>
    <dimen name="dp_253">253dp</dimen>
    <dimen name="dp_254">254dp</dimen>
    <dimen name="dp_255">255dp</dimen>
    <dimen name="dp_256">256dp</dimen>
    <dimen name="dp_257">257dp</dimen>
    <dimen name="dp_258">258dp</dimen>
    <dimen name="dp_259">259dp</dimen>
    <dimen name="dp_260">260dp</dimen>
    <dimen name="dp_261">261dp</dimen>
    <dimen name="dp_262">262dp</dimen>
    <dimen name="dp_263">263dp</dimen>
    <dimen name="dp_264">264dp</dimen>
    <dimen name="dp_265">265dp</dimen>
    <dimen name="dp_266">266dp</dimen>
    <dimen name="dp_267">267dp</dimen>
    <dimen name="dp_268">268dp</dimen>
    <dimen name="dp_269">269dp</dimen>
    <dimen name="dp_270">270dp</dimen>
    <dimen name="dp_271">271dp</dimen>
    <dimen name="dp_272">272dp</dimen>
    <dimen name="dp_273">273dp</dimen>
    <dimen name="dp_274">274dp</dimen>
    <dimen name="dp_275">275dp</dimen>
    <dimen name="dp_276">276dp</dimen>
    <dimen name="dp_277">277dp</dimen>
    <dimen name="dp_278">278dp</dimen>
    <dimen name="dp_279">279dp</dimen>
    <dimen name="dp_280">280dp</dimen>
    <dimen name="dp_281">281dp</dimen>
    <dimen name="dp_282">282dp</dimen>
    <dimen name="dp_283">283dp</dimen>
    <dimen name="dp_284">284dp</dimen>
    <dimen name="dp_285">285dp</dimen>
    <dimen name="dp_286">286dp</dimen>
    <dimen name="dp_287">287dp</dimen>
    <dimen name="dp_288">288dp</dimen>
    <dimen name="dp_289">289dp</dimen>
    <dimen name="dp_290">290dp</dimen>
    <dimen name="dp_291">291dp</dimen>
    <dimen name="dp_292">292dp</dimen>
    <dimen name="dp_293">293dp</dimen>
    <dimen name="dp_294">294dp</dimen>
    <dimen name="dp_295">295dp</dimen>
    <dimen name="dp_296">296dp</dimen>
    <dimen name="dp_297">297dp</dimen>
    <dimen name="dp_298">298dp</dimen>
    <dimen name="dp_299">299dp</dimen>
    <dimen name="dp_300">300dp</dimen>
    <dimen name="dp_301">301dp</dimen>
    <dimen name="dp_302">302dp</dimen>
    <dimen name="dp_303">303dp</dimen>
    <dimen name="dp_304">304dp</dimen>
    <dimen name="dp_305">305dp</dimen>
    <dimen name="dp_306">306dp</dimen>
    <dimen name="dp_307">307dp</dimen>
    <dimen name="dp_308">308dp</dimen>
    <dimen name="dp_309">309dp</dimen>
    <dimen name="dp_310">310dp</dimen>
    <dimen name="dp_311">311dp</dimen>
    <dimen name="dp_312">312dp</dimen>
    <dimen name="dp_313">313dp</dimen>
    <dimen name="dp_314">314dp</dimen>
    <dimen name="dp_315">315dp</dimen>
    <dimen name="dp_316">316dp</dimen>
    <dimen name="dp_317">317dp</dimen>
    <dimen name="dp_318">318dp</dimen>
    <dimen name="dp_319">319dp</dimen>
    <dimen name="dp_320">320dp</dimen>
    <dimen name="dp_321">321dp</dimen>
    <dimen name="dp_322">322dp</dimen>
    <dimen name="dp_323">323dp</dimen>
    <dimen name="dp_324">324dp</dimen>
    <dimen name="dp_325">325dp</dimen>
    <dimen name="dp_326">326dp</dimen>
    <dimen name="dp_327">327dp</dimen>
    <dimen name="dp_328">328dp</dimen>
    <dimen name="dp_329">329dp</dimen>
    <dimen name="dp_330">330dp</dimen>
    <dimen name="dp_331">331dp</dimen>
    <dimen name="dp_332">332dp</dimen>
    <dimen name="dp_333">333dp</dimen>
    <dimen name="dp_334">334dp</dimen>
    <dimen name="dp_335">335dp</dimen>
    <dimen name="dp_336">336dp</dimen>
    <dimen name="dp_337">337dp</dimen>
    <dimen name="dp_338">338dp</dimen>
    <dimen name="dp_339">339dp</dimen>
    <dimen name="dp_340">340dp</dimen>
    <dimen name="dp_341">341dp</dimen>
    <dimen name="dp_342">342dp</dimen>
    <dimen name="dp_343">343dp</dimen>
    <dimen name="dp_344">344dp</dimen>
    <dimen name="dp_345">345dp</dimen>
    <dimen name="dp_346">346dp</dimen>
    <dimen name="dp_347">347dp</dimen>
    <dimen name="dp_348">348dp</dimen>
    <dimen name="dp_349">349dp</dimen>
    <dimen name="dp_350">350dp</dimen>
    <dimen name="dp_351">351dp</dimen>
    <dimen name="dp_352">352dp</dimen>
    <dimen name="dp_353">353dp</dimen>
    <dimen name="dp_354">354dp</dimen>
    <dimen name="dp_355">355dp</dimen>
    <dimen name="dp_356">356dp</dimen>
    <dimen name="dp_357">357dp</dimen>
    <dimen name="dp_358">358dp</dimen>
    <dimen name="dp_359">359dp</dimen>
    <dimen name="dp_360">360dp</dimen>
    <dimen name="dp_365">365dp</dimen>
    <dimen name="dp_370">370dp</dimen>
    <dimen name="dp_400">400dp</dimen>
    <dimen name="dp_410">410dp</dimen>
    <dimen name="dp_422">422dp</dimen>
    <dimen name="dp_472">472dp</dimen>
    <dimen name="dp_500">500dp</dimen>
    <dimen name="dp_600">600dp</dimen>
    <dimen name="dp_640">640dp</dimen>
    <dimen name="dp_720">720dp</dimen>
    <!--字体-->
    <dimen name="sp_6">6sp</dimen>
    <dimen name="sp_7">7sp</dimen>
    <dimen name="sp_8">8sp</dimen>
    <dimen name="sp_9">9sp</dimen>
    <dimen name="sp_10">10sp</dimen>
    <dimen name="sp_11">11sp</dimen>
    <dimen name="sp_12">12sp</dimen>
    <dimen name="sp_13">13sp</dimen>
    <dimen name="sp_14">14sp</dimen>
    <dimen name="sp_15">15sp</dimen>
    <dimen name="sp_16">16sp</dimen>
    <dimen name="sp_17">17sp</dimen>
    <dimen name="sp_18">18sp</dimen>
    <dimen name="sp_19">19sp</dimen>
    <dimen name="sp_20">20sp</dimen>
    <dimen name="sp_21">21sp</dimen>
    <dimen name="sp_22">22sp</dimen>
    <dimen name="sp_23">23sp</dimen>
    <dimen name="sp_24">24sp</dimen>
    <dimen name="sp_25">25sp</dimen>
    <dimen name="sp_26">26sp</dimen>
    <dimen name="sp_27">27sp</dimen>
    <dimen name="sp_28">28sp</dimen>
    <dimen name="sp_29">29sp</dimen>
    <dimen name="sp_30">30sp</dimen>
    <dimen name="sp_31">31sp</dimen>
    <dimen name="sp_32">32sp</dimen>
    <dimen name="sp_33">33sp</dimen>
    <dimen name="sp_34">34sp</dimen>
    <dimen name="sp_35">35sp</dimen>
    <dimen name="sp_36">36sp</dimen>
    <dimen name="sp_37">37sp</dimen>
    <dimen name="sp_38">38sp</dimen>
    <dimen name="sp_40">40sp</dimen>
    <dimen name="sp_42">42sp</dimen>
    <dimen name="sp_48">48sp</dimen>
</resources>


弹窗页面的布局如下:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/transparent"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="@dimen/dp_8"
        android:background="@mipmap/icon_add_bg_9"
        android:orientation="vertical"
        android:paddingBottom="@dimen/dp_20"
        android:paddingTop="@dimen/dp_20">
        <TextView
            android:id="@+id/tv_change_city"
            android:gravity="center"
            android:layout_width="@dimen/dp_140"
            android:layout_height="@dimen/dp_48"
            android:text="切换城市"
            android:foreground="@drawable/bg_white"
            android:textColor="@color/black"
            android:textSize="@dimen/sp_16"/>
        <TextView
            android:id="@+id/tv_change_bg"
            android:gravity="center"
            android:layout_width="@dimen/dp_140"
            android:layout_height="@dimen/dp_48"
            android:text="切换背景"
            android:foreground="@drawable/bg_white"
            android:textColor="@color/black"
            android:textSize="@dimen/sp_16"/>
        <TextView
            android:id="@+id/tv_more"
            android:gravity="center"
            android:layout_width="@dimen/dp_140"
            android:layout_height="@dimen/dp_48"
            android:text="更多功能"
            android:foreground="@drawable/bg_white"
            android:textColor="@color/black"
            android:textSize="@dimen/sp_16"/>
    </LinearLayout>
</LinearLayout>


然后是对activity_main.xml文件的修改


20200508162159523.png


这里修改了原来的id和src里面的图片,增加了点击的效果

icon_add.png

20200508162354550.png

selector_bg_img.xml,点击之后背景变色,增加用户体验


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape>
            <solid android:color="#22000000"/>
        </shape>
    </item>
    <item>
        <shape>
            <solid android:color="#00000000"/>
        </shape>
    </item>
</selector>


MainActivity.java


20200508162655803.png


这里我是把原来的id注释掉,不过我没有删掉,因为我要让你们知道是怎么样一个过程,你们是可以直接替换的,不替换会报错了,当然你不改Id就不会报错,但是id的命名和现在是意思对不上,会对其他人造成困扰,严谨一点就修改ID。


20200508162930979.png

更改点击之后的弹窗。

20200508163132765.png


要显示弹窗一些基本的配置必不可少,这里用到了一个动画工具类AnimationUtil,代码如下:

package com.llw.mvplibrary.utils;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
/**
 * 动画工具类
 * UpdateListener: 动画过程中通过添加此监听来回调数据
 * EndListener: 动画结束的时候通过此监听器来做一些处理
 */
public class AnimationUtil {
    private ValueAnimator valueAnimator;
    private UpdateListener updateListener;
    private EndListener endListener;
    private long duration;
    private float start;
    private float end;
    private Interpolator interpolator = new LinearInterpolator();
    public AnimationUtil() {
        duration = 1000; //默认动画时常1s
        start = 0.0f;
        end = 1.0f;
        interpolator = new LinearInterpolator();// 匀速的插值器
    }
    public void setDuration(int timeLength) {
        duration = timeLength;
    }
    public void setValueAnimator(float start, float end, long duration) {
        this.start = start;
        this.end = end;
        this.duration = duration;
    }
    public void setInterpolator(Interpolator interpolator) {
        this.interpolator = interpolator;
    }
    public void startAnimator() {
        if (valueAnimator != null){
            valueAnimator = null;
        }
        valueAnimator = ValueAnimator.ofFloat(start, end);
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(interpolator);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                if (updateListener == null) {
                    return;
                }
                float cur = (float) valueAnimator.getAnimatedValue();
                updateListener.progress(cur);
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {}
            @Override
            public void onAnimationEnd(Animator animator) {
                if(endListener == null){
                    return;
                }
                endListener.endUpdate(animator);
            }
            @Override
            public void onAnimationCancel(Animator animator) {}
            @Override
            public void onAnimationRepeat(Animator animator) {}
        });
        valueAnimator.start();
    }
    public void addUpdateListener(UpdateListener updateListener) {
        this.updateListener = updateListener;
    }
    public void addEndListner(EndListener endListener){
        this.endListener = endListener;
    }
    public interface EndListener {
        void endUpdate(Animator animator);
    }
    public interface UpdateListener {
        void progress(float progress);
    }
}


20200508163402724.png


然后写三个方法,一个显示弹窗,及控制里面的点击事件、计算动画时间、第三个修改背景的透明度类似蒙版的效果。

20200508163548479.png


这三个方法的代码我都会贴上来。不过首先,先增加弹窗出现和关闭的动画效果。


20200508163954348.png


这张图告诉你在什么地方添加这个样式


  <!--右上角加号点击弹窗动画效果-->
    <style name="pop_add">
        <item name="android:windowEnterAnimation">@anim/pop_add_show</item>
        <item name="android:windowExitAnimation">@anim/pop_add_hide</item>
    </style>


pop_add_show.xml 显示动画


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="500"
        android:fromAlpha="0.0"
        android:toAlpha="1.0"/>
    <scale
        android:duration="500"
        android:fromXScale="0"
        android:fromYScale="0"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:pivotX="85%"
        android:pivotY="0%"
        android:toXScale="1.0"
        android:toYScale="1.0"/>
</set>


pop_add_hide.xml 隐藏动画


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="500"
        android:fromAlpha="1.0"
        android:toAlpha="0.0"/>
    <scale
        android:duration="500"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:pivotX="85%"
        android:pivotY="0%"
        android:toXScale="0"
        android:toYScale="0"/>
</set>


然后贴一下三个方法的代码:


showAddWindow方法

  /**
     * 更多功能弹窗,因为区别于我原先写的弹窗
     */
    private void showAddWindow() {
        // 设置布局文件
        mPopupWindow.setContentView(LayoutInflater.from(this).inflate(R.layout.window_add, null));// 为了避免部分机型不显示,我们需要重新设置一下宽高
        mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
        mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        mPopupWindow.setBackgroundDrawable(new ColorDrawable(0x0000));// 设置pop透明效果
        mPopupWindow.setAnimationStyle(R.style.pop_add);// 设置pop出入动画
        mPopupWindow.setFocusable(true);// 设置pop获取焦点,如果为false点击返回按钮会退出当前Activity,如果pop中有Editor的话,focusable必须要为true
        mPopupWindow.setTouchable(true);// 设置pop可点击,为false点击事件无效,默认为true
        mPopupWindow.setOutsideTouchable(true);// 设置点击pop外侧消失,默认为false;在focusable为true时点击外侧始终消失
        mPopupWindow.showAsDropDown(ivAdd, -100, 0);// 相对于 + 号正下面,同时可以设置偏移量
        // 设置pop关闭监听,用于改变背景透明度
        mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {//关闭弹窗
            @Override
            public void onDismiss() {
                toggleBright();
            }
        });
        //绑定布局中的控件
        TextView changeCity = mPopupWindow.getContentView().findViewById(R.id.tv_change_city);
        TextView changeBg = mPopupWindow.getContentView().findViewById(R.id.tv_change_bg);
        TextView more = mPopupWindow.getContentView().findViewById(R.id.tv_more);
        changeCity.setOnClickListener(view -> {//切换城市
            showCityWindow();
            mPopupWindow.dismiss();
        });
        changeBg.setOnClickListener(view -> {//切换背景
            ToastUtils.showShortToast(context,"你点击了切换背景");
            mPopupWindow.dismiss();
        });
        more.setOnClickListener(view -> {//更多功能
            ToastUtils.showShortToast(context,"如果你有什么好的建议,可以博客留言哦!");
            mPopupWindow.dismiss();
        });
    }


计算动画时间


  /**
     * 计算动画时间
     */
    private void toggleBright() {
        // 三个参数分别为:起始值 结束值 时长,那么整个动画回调过来的值就是从0.5f--1f的
        animUtil.setValueAnimator(START_ALPHA, END_ALPHA, DURATION);
        animUtil.addUpdateListener(new AnimationUtil.UpdateListener() {
            @Override
            public void progress(float progress) {
                // 此处系统会根据上述三个值,计算每次回调的值是多少,我们根据这个值来改变透明度
                bgAlpha = bright ? progress : (START_ALPHA + END_ALPHA - progress);
                backgroundAlpha(bgAlpha);
            }
        });
        animUtil.addEndListner(new AnimationUtil.EndListener() {
            @Override
            public void endUpdate(Animator animator) {
                // 在一次动画结束的时候,翻转状态
                bright = !bright;
            }
        });
        animUtil.startAnimator();
    }


此方法用于改变背景的透明度


  /**
     * 此方法用于改变背景的透明度,从而达到“变暗”的效果
     */
    private void backgroundAlpha(float bgAlpha) {
        WindowManager.LayoutParams lp = getWindow().getAttributes();
        // 0.0-1.0
        lp.alpha = bgAlpha;
        getWindow().setAttributes(lp);
        // everything behind this window will be dimmed.
        // 此方法用来设置浮动层,防止部分手机变暗无效
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    }


这几个方法借鉴了网络上的代码。


效果图如下:

20200508165157329.png


如果你在写的过程中遇到任何问题,可以直接评论或者给我发邮件。

接下来就是切换背景了


切换背景


切换背景的业务代码,我当然不可能也在MainActivity中写,因为现在里面的代码已经够多了,所以就要新建一个页面。在项目的包下新建一个ui包,用于存放除MainActivity之外的所有Activity。这样会比较规范,至于为什么不把MainActivity也放进去,因为目前我还不想放进去。

鼠标右键点击ui → New → Activity → Empty Activity


20200508165943948.png

20200508170125819.png


Next即可

20200508165655774.png


创建好之后修改布局。

修改布局之前,这个要更改一个开关按钮的样式。


2020050817052547.png


首先是增加样式代码:


  <!--开关按钮-->
    <declare-styleable name="SwitchButton">
        <attr name="sb_shadow_radius" format="reference|dimension"/>
        <attr name="sb_shadow_offset" format="reference|dimension"/>
        <attr name="sb_shadow_color" format="reference|color"/>
        <attr name="sb_uncheck_color" format="reference|color"/>
        <attr name="sb_checked_color" format="reference|color"/>
        <attr name="sb_border_width" format="reference|dimension"/>
        <attr name="sb_checkline_color" format="reference|color"/>
        <attr name="sb_checkline_width" format="reference|dimension"/>
        <attr name="sb_uncheckcircle_color" format="reference|color"/>
        <attr name="sb_uncheckcircle_width" format="reference|dimension"/>
        <attr name="sb_uncheckcircle_radius" format="reference|dimension"/>
        <attr name="sb_checked" format="reference|boolean"/>
        <attr name="sb_shadow_effect" format="reference|boolean"/>
        <attr name="sb_effect_duration" format="reference|integer"/>
        <attr name="sb_button_color" format="reference|color"/>
        <attr name="sb_show_indicator" format="reference|boolean"/>
        <attr name="sb_background" format="reference|color"/>
        <attr name="sb_enable_effect" format="reference|boolean"/>
    </declare-styleable>

然后自定义View


20200508170651606.png


SwitchButton.java代码如下:


package com.llw.mvplibrary.view;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Checkable;
import com.llw.mvplibrary.R;
/**
 * SwitchButton.
 */
public class SwitchButton extends View implements Checkable {
    private static final int DEFAULT_WIDTH = dp2pxInt(58);
    private static final int DEFAULT_HEIGHT = dp2pxInt(36);
    /**
     * 动画状态:
     * 1.静止
     * 2.进入拖动
     * 3.处于拖动
     * 4.拖动-复位
     * 5.拖动-切换
     * 6.点击切换
     * **/
    private final int ANIMATE_STATE_NONE = 0;
    private final int ANIMATE_STATE_PENDING_DRAG = 1;
    private final int ANIMATE_STATE_DRAGING = 2;
    private final int ANIMATE_STATE_PENDING_RESET = 3;
    private final int ANIMATE_STATE_PENDING_SETTLE = 4;
    private final int ANIMATE_STATE_SWITCH = 5;
    public SwitchButton(Context context) {
        super(context);
        init(context, null);
    }
    public SwitchButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }
    public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }
    @Override
    public final void setPadding(int left, int top, int right, int bottom) {
        super.setPadding(0, 0, 0, 0);
    }
    /**
     * 初始化参数
     */
    private void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = null;
        if(attrs != null){
            typedArray = context.obtainStyledAttributes(attrs, R.styleable.SwitchButton);
        }
        shadowEffect = optBoolean(typedArray,
                R.styleable.SwitchButton_sb_shadow_effect,
                true);
        uncheckCircleColor = optColor(typedArray,
                R.styleable.SwitchButton_sb_uncheckcircle_color,
                0XffAAAAAA);//0XffAAAAAA;
        uncheckCircleWidth = optPixelSize(typedArray,
                R.styleable.SwitchButton_sb_uncheckcircle_width,
                dp2pxInt(1.5f));//dp2pxInt(1.5f);
        uncheckCircleOffsetX = dp2px(10);
        uncheckCircleRadius = optPixelSize(typedArray,
                R.styleable.SwitchButton_sb_uncheckcircle_radius,
                dp2px(4));//dp2px(4);
        checkedLineOffsetX = dp2px(4);
        checkedLineOffsetY = dp2px(4);
        shadowRadius = optPixelSize(typedArray,
                R.styleable.SwitchButton_sb_shadow_radius,
                dp2pxInt(2.5f));//dp2pxInt(2.5f);
        shadowOffset = optPixelSize(typedArray,
                R.styleable.SwitchButton_sb_shadow_offset,
                dp2pxInt(1.5f));//dp2pxInt(1.5f);
        shadowColor = optColor(typedArray,
                R.styleable.SwitchButton_sb_shadow_color,
                0X33000000);//0X33000000;
        uncheckColor = optColor(typedArray,
                R.styleable.SwitchButton_sb_uncheck_color,
                0XffDDDDDD);//0XffDDDDDD;
        checkedColor = optColor(typedArray,
                R.styleable.SwitchButton_sb_checked_color,
                0Xff51d367);//0Xff51d367;
        borderWidth = optPixelSize(typedArray,
                R.styleable.SwitchButton_sb_border_width,
                dp2pxInt(1));//dp2pxInt(1);
        checkLineColor = optColor(typedArray,
                R.styleable.SwitchButton_sb_checkline_color,
                Color.WHITE);//Color.WHITE;
        checkLineWidth = optPixelSize(typedArray,
                R.styleable.SwitchButton_sb_checkline_width,
                dp2pxInt(1f));//dp2pxInt(1.0f);
        checkLineLength = dp2px(6);
        int buttonColor = optColor(typedArray,
                R.styleable.SwitchButton_sb_button_color,
                Color.WHITE);//Color.WHITE;
        int effectDuration = optInt(typedArray,
                R.styleable.SwitchButton_sb_effect_duration,
                300);//300;
        isChecked = optBoolean(typedArray,
                R.styleable.SwitchButton_sb_checked,
                false);
        showIndicator = optBoolean(typedArray,
                R.styleable.SwitchButton_sb_show_indicator,
                true);
        background = optColor(typedArray,
                R.styleable.SwitchButton_sb_background,
                Color.WHITE);//Color.WHITE;
        enableEffect = optBoolean(typedArray,
                R.styleable.SwitchButton_sb_enable_effect,
                true);
        if(typedArray != null){
            typedArray.recycle();
        }
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        buttonPaint.setColor(buttonColor);
        if(shadowEffect){
            buttonPaint.setShadowLayer(
                    shadowRadius,
                    0, shadowOffset,
                    shadowColor);
        }
        viewState = new ViewState();
        beforeState = new ViewState();
        afterState = new ViewState();
        valueAnimator = ValueAnimator.ofFloat(0f, 1f);
        valueAnimator.setDuration(effectDuration);
        valueAnimator.setRepeatCount(0);
        valueAnimator.addUpdateListener(animatorUpdateListener);
        valueAnimator.addListener(animatorListener);
        super.setClickable(true);
        this.setPadding(0, 0, 0, 0);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            setLayerType(LAYER_TYPE_SOFTWARE, null);
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if(widthMode == MeasureSpec.UNSPECIFIED
                || widthMode == MeasureSpec.AT_MOST){
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_WIDTH, MeasureSpec.EXACTLY);
        }
        if(heightMode == MeasureSpec.UNSPECIFIED
                || heightMode == MeasureSpec.AT_MOST){
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_HEIGHT, MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        float viewPadding = Math.max(shadowRadius + shadowOffset, borderWidth);
        height = h - viewPadding - viewPadding;
        width = w - viewPadding - viewPadding;
        viewRadius = height * .5f;
        buttonRadius = viewRadius - borderWidth;
        left = viewPadding;
        top = viewPadding;
        right = w - viewPadding;
        bottom = h - viewPadding;
        centerX = (left + right) * .5f;
        centerY = (top + bottom) * .5f;
        buttonMinX = left + viewRadius;
        buttonMaxX = right - viewRadius;
        if(isChecked()){
            setCheckedViewState(viewState);
        }else{
            setUncheckViewState(viewState);
        }
        isUiInited = true;
        postInvalidate();
    }
    /**
     * @param viewState
     */
    private void setUncheckViewState(ViewState viewState){
        viewState.radius = 0;
        viewState.checkStateColor = uncheckColor;
        viewState.checkedLineColor = Color.TRANSPARENT;
        viewState.buttonX = buttonMinX;
    }
    /**
     * @param viewState
     */
    private void setCheckedViewState(ViewState viewState){
        viewState.radius = viewRadius;
        viewState.checkStateColor = checkedColor;
        viewState.checkedLineColor = checkLineColor;
        viewState.buttonX = buttonMaxX;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setStrokeWidth(borderWidth);
        paint.setStyle(Paint.Style.FILL);
        //绘制白色背景
        paint.setColor(background);
        drawRoundRect(canvas,
                left, top, right, bottom,
                viewRadius, paint);
        //绘制关闭状态的边框
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(uncheckColor);
        drawRoundRect(canvas,
                left, top, right, bottom,
                viewRadius, paint);
        //绘制小圆圈
        if(showIndicator){
            drawUncheckIndicator(canvas);
        }
        //绘制开启背景色
        float des = viewState.radius * .5f;//[0-backgroundRadius*0.5f]
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(viewState.checkStateColor);
        paint.setStrokeWidth(borderWidth + des * 2f);
        drawRoundRect(canvas,
                left + des, top + des, right - des, bottom - des,
                viewRadius, paint);
        //绘制按钮左边绿色长条遮挡
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(1);
        drawArc(canvas,
                left, top,
                left + 2 * viewRadius, top + 2 * viewRadius,
                90, 180, paint);
        canvas.drawRect(
                left + viewRadius, top,
                viewState.buttonX, top + 2 * viewRadius,
                paint);
        //绘制小线条
        if(showIndicator){
            drawCheckedIndicator(canvas);
        }
        //绘制按钮
        drawButton(canvas, viewState.buttonX, centerY);
    }
    /**
     * 绘制选中状态指示器
     * @param canvas
     */
    protected void drawCheckedIndicator(Canvas canvas) {
        drawCheckedIndicator(canvas,
                viewState.checkedLineColor,
                checkLineWidth,
                left + viewRadius - checkedLineOffsetX, centerY - checkLineLength,
                left + viewRadius - checkedLineOffsetY, centerY + checkLineLength,
                paint);
    }
    /**
     * 绘制选中状态指示器
     * @param canvas
     * @param color
     * @param lineWidth
     * @param sx
     * @param sy
     * @param ex
     * @param ey
     * @param paint
     */
    protected void drawCheckedIndicator(Canvas canvas,
                                        int color,
                                        float lineWidth,
                                        float sx, float sy, float ex, float ey,
                                        Paint paint) {
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(color);
        paint.setStrokeWidth(lineWidth);
        canvas.drawLine(
                sx, sy, ex, ey,
                paint);
    }
    /**
     * 绘制关闭状态指示器
     * @param canvas
     */
    private void drawUncheckIndicator(Canvas canvas) {
        drawUncheckIndicator(canvas,
                uncheckCircleColor,
                uncheckCircleWidth,
                right - uncheckCircleOffsetX, centerY,
                uncheckCircleRadius,
                paint);
    }
    /**
     * 绘制关闭状态指示器
     * @param canvas
     * @param color
     * @param lineWidth
     * @param centerX
     * @param centerY
     * @param radius
     * @param paint
     */
    protected void drawUncheckIndicator(Canvas canvas,
                                        int color,
                                        float lineWidth,
                                        float centerX, float centerY,
                                        float radius,
                                        Paint paint) {
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(color);
        paint.setStrokeWidth(lineWidth);
        canvas.drawCircle(centerX, centerY, radius, paint);
    }
    /**
     * @param canvas
     * @param left
     * @param top
     * @param right
     * @param bottom
     * @param startAngle
     * @param sweepAngle
     * @param paint
     */
    private void drawArc(Canvas canvas,
                         float left, float top,
                         float right, float bottom,
                         float startAngle, float sweepAngle,
                         Paint paint){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            canvas.drawArc(left, top, right, bottom,
                    startAngle, sweepAngle, true, paint);
        }else{
            rect.set(left, top, right, bottom);
            canvas.drawArc(rect,
                    startAngle, sweepAngle, true, paint);
        }
    }
    /**
     * @param canvas
     * @param left
     * @param top
     * @param right
     * @param bottom
     * @param backgroundRadius
     * @param paint
     */
    private void drawRoundRect(Canvas canvas,
                               float left, float top,
                               float right, float bottom,
                               float backgroundRadius,
                               Paint paint){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            canvas.drawRoundRect(left, top, right, bottom,
                    backgroundRadius, backgroundRadius, paint);
        }else{
            rect.set(left, top, right, bottom);
            canvas.drawRoundRect(rect,
                    backgroundRadius, backgroundRadius, paint);
        }
    }
    /**
     * @param canvas
     * @param x px
     * @param y px
     */
    private void drawButton(Canvas canvas, float x, float y) {
        canvas.drawCircle(x, y, buttonRadius, buttonPaint);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1);
        paint.setColor(0XffDDDDDD);
        canvas.drawCircle(x, y, buttonRadius, paint);
    }
    @Override
    public void setChecked(boolean checked) {
        if(checked == isChecked()){
            postInvalidate();
            return;
        }
        toggle(enableEffect, false);
    }
    @Override
    public boolean isChecked() {
        return isChecked;
    }
    @Override
    public void toggle() {
        toggle(true);
    }
    /**
     * 切换状态
     * @param animate
     */
    public void toggle(boolean animate) {
        toggle(animate, true);
    }
    private void toggle(boolean animate, boolean broadcast) {
        if(!isEnabled()){return;}
        if(isEventBroadcast){
            throw new RuntimeException("should NOT switch the state in method: [onCheckedChanged]!");
        }
        if(!isUiInited){
            isChecked = !isChecked;
            if(broadcast){
                broadcastEvent();
            }
            return;
        }
        if(valueAnimator.isRunning()){
            valueAnimator.cancel();
        }
        if(!enableEffect || !animate){
            isChecked = !isChecked;
            if(isChecked()){
                setCheckedViewState(viewState);
            }else{
                setUncheckViewState(viewState);
            }
            postInvalidate();
            if(broadcast){
                broadcastEvent();
            }
            return;
        }
        animateState = ANIMATE_STATE_SWITCH;
        beforeState.copy(viewState);
        if(isChecked()){
            //切换到unchecked
            setUncheckViewState(afterState);
        }else{
            setCheckedViewState(afterState);
        }
        valueAnimator.start();
    }
    /**
     *
     */
    private void broadcastEvent() {
        if(onCheckedChangeListener != null){
            isEventBroadcast = true;
            onCheckedChangeListener.onCheckedChanged(this, isChecked());
        }
        isEventBroadcast = false;
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(!isEnabled()){return false;}
        int actionMasked = event.getActionMasked();
        switch (actionMasked){
            case MotionEvent.ACTION_DOWN:{
                isTouchingDown = true;
                touchDownTime = System.currentTimeMillis();
                //取消准备进入拖动状态
                removeCallbacks(postPendingDrag);
                //预设100ms进入拖动状态
                postDelayed(postPendingDrag, 100);
                break;
            }
            case MotionEvent.ACTION_MOVE:{
                float eventX = event.getX();
                if(isPendingDragState()){
                    //在准备进入拖动状态过程中,可以拖动按钮位置
                    float fraction = eventX / getWidth();
                    fraction = Math.max(0f, Math.min(1f, fraction));
                    viewState.buttonX = buttonMinX
                            + (buttonMaxX - buttonMinX)
                            * fraction;
                }else if(isDragState()){
                    //拖动按钮位置,同时改变对应的背景颜色
                    float fraction = eventX / getWidth();
                    fraction = Math.max(0f, Math.min(1f, fraction));
                    viewState.buttonX = buttonMinX
                            + (buttonMaxX - buttonMinX)
                            * fraction;
                    viewState.checkStateColor = (int) argbEvaluator.evaluate(
                            fraction,
                            uncheckColor,
                            checkedColor
                    );
                    postInvalidate();
                }
                break;
            }
            case MotionEvent.ACTION_UP:{
                isTouchingDown = false;
                //取消准备进入拖动状态
                removeCallbacks(postPendingDrag);
                if(System.currentTimeMillis() - touchDownTime <= 300){
                    //点击时间小于300ms,认为是点击操作
                    toggle();
                }else if(isDragState()){
                    //在拖动状态,计算按钮位置,设置是否切换状态
                    float eventX = event.getX();
                    float fraction = eventX / getWidth();
                    fraction = Math.max(0f, Math.min(1f, fraction));
                    boolean newCheck = fraction > .5f;
                    if(newCheck == isChecked()){
                        pendingCancelDragState();
                    }else{
                        isChecked = newCheck;
                        pendingSettleState();
                    }
                }else if(isPendingDragState()){
                    //在准备进入拖动状态过程中,取消之,复位
                    pendingCancelDragState();
                }
                break;
            }
            case MotionEvent.ACTION_CANCEL:{
                isTouchingDown = false;
                removeCallbacks(postPendingDrag);
                if(isPendingDragState()
                        || isDragState()){
                    //复位
                    pendingCancelDragState();
                }
                break;
            }
        }
        return true;
    }
    /**
     * 是否在动画状态
     * @return
     */
    private boolean isInAnimating(){
        return animateState != ANIMATE_STATE_NONE;
    }
    /**
     * 是否在进入拖动或离开拖动状态
     * @return
     */
    private boolean isPendingDragState(){
        return animateState == ANIMATE_STATE_PENDING_DRAG
                || animateState == ANIMATE_STATE_PENDING_RESET;
    }
    /**
     * 是否在手指拖动状态
     * @return
     */
    private boolean isDragState(){
        return animateState == ANIMATE_STATE_DRAGING;
    }
    /**
     * 设置是否启用阴影效果
     * @param shadowEffect true.启用
     */
    public void setShadowEffect(boolean shadowEffect) {
        if(this.shadowEffect == shadowEffect){return;}
        this.shadowEffect = shadowEffect;
        if(this.shadowEffect){
            buttonPaint.setShadowLayer(
                    shadowRadius,
                    0, shadowOffset,
                    shadowColor);
        }else{
            buttonPaint.setShadowLayer(
                    0,
                    0, 0,
                    0);
        }
    }
    public void setEnableEffect(boolean enable){
        this.enableEffect = enable;
    }
    /**
     * 开始进入拖动状态
     */
    private void pendingDragState() {
        if(isInAnimating()){return;}
        if(!isTouchingDown){return;}
        if(valueAnimator.isRunning()){
            valueAnimator.cancel();
        }
        animateState = ANIMATE_STATE_PENDING_DRAG;
        beforeState.copy(viewState);
        afterState.copy(viewState);
        if(isChecked()){
            afterState.checkStateColor = checkedColor;
            afterState.buttonX = buttonMaxX;
            afterState.checkedLineColor = checkedColor;
        }else{
            afterState.checkStateColor = uncheckColor;
            afterState.buttonX = buttonMinX;
            afterState.radius = viewRadius;
        }
        valueAnimator.start();
    }
    /**
     * 取消拖动状态
     */
    private void pendingCancelDragState() {
        if(isDragState() || isPendingDragState()){
            if(valueAnimator.isRunning()){
                valueAnimator.cancel();
            }
            animateState = ANIMATE_STATE_PENDING_RESET;
            beforeState.copy(viewState);
            if(isChecked()){
                setCheckedViewState(afterState);
            }else{
                setUncheckViewState(afterState);
            }
            valueAnimator.start();
        }
    }
    /**
     * 动画-设置新的状态
     */
    private void pendingSettleState() {
        if(valueAnimator.isRunning()){
            valueAnimator.cancel();
        }
        animateState = ANIMATE_STATE_PENDING_SETTLE;
        beforeState.copy(viewState);
        if(isChecked()){
            setCheckedViewState(afterState);
        }else{
            setUncheckViewState(afterState);
        }
        valueAnimator.start();
    }
    @Override
    public final void setOnClickListener(OnClickListener l) {}
    @Override
    public final void setOnLongClickListener(OnLongClickListener l) {}
    public void setOnCheckedChangeListener(OnCheckedChangeListener l){
        onCheckedChangeListener = l;
    }
    public interface OnCheckedChangeListener{
        void onCheckedChanged(SwitchButton view, boolean isChecked);
    }
    /*******************************************************/
    private static float dp2px(float dp){
        Resources r = Resources.getSystem();
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
    }
    private static int dp2pxInt(float dp){
        return (int) dp2px(dp);
    }
    private static int optInt(TypedArray typedArray,
                              int index,
                              int def) {
        if(typedArray == null){return def;}
        return typedArray.getInt(index, def);
    }
    private static float optPixelSize(TypedArray typedArray,
                                      int index,
                                      float def) {
        if(typedArray == null){return def;}
        return typedArray.getDimension(index, def);
    }
    private static int optPixelSize(TypedArray typedArray,
                                    int index,
                                    int def) {
        if(typedArray == null){return def;}
        return typedArray.getDimensionPixelOffset(index, def);
    }
    private static int optColor(TypedArray typedArray,
                                int index,
                                int def) {
        if(typedArray == null){return def;}
        return typedArray.getColor(index, def);
    }
    private static boolean optBoolean(TypedArray typedArray,
                                      int index,
                                      boolean def) {
        if(typedArray == null){return def;}
        return typedArray.getBoolean(index, def);
    }
    /*******************************************************/
    /**
     * 阴影半径
     */
    private int shadowRadius;
    /**
     * 阴影Y偏移px
     */
    private int shadowOffset;
    /**
     * 阴影颜色
     */
    private int shadowColor ;
    /**
     * 背景半径
     */
    private float viewRadius;
    /**
     * 按钮半径
     */
    private float buttonRadius;
    /**
     * 背景高
     */
    private float height ;
    /**
     * 背景宽
     */
    private float width;
    /**
     * 背景位置
     */
    private float left   ;
    private float top    ;
    private float right  ;
    private float bottom ;
    private float centerX;
    private float centerY;
    /**
     * 背景底色
     */
    private int background;
    /**
     * 背景关闭颜色
     */
    private int uncheckColor;
    /**
     * 背景打开颜色
     */
    private int checkedColor;
    /**
     * 边框宽度px
     */
    private int borderWidth;
    /**
     * 打开指示线颜色
     */
    private int checkLineColor;
    /**
     * 打开指示线宽
     */
    private int checkLineWidth;
    /**
     * 打开指示线长
     */
    private float checkLineLength;
    /**
     * 关闭圆圈颜色
     */
    private int uncheckCircleColor;
    /**
     *关闭圆圈线宽
     */
    private int uncheckCircleWidth;
    /**
     *关闭圆圈位移X
     */
    private float uncheckCircleOffsetX;
    /**
     *关闭圆圈半径
     */
    private float uncheckCircleRadius;
    /**
     *打开指示线位移X
     */
    private float checkedLineOffsetX;
    /**
     *打开指示线位移Y
     */
    private float checkedLineOffsetY;
    /**
     * 按钮最左边
     */
    private float buttonMinX;
    /**
     * 按钮最右边
     */
    private float buttonMaxX;
    /**
     * 按钮画笔
     */
    private Paint buttonPaint;
    /**
     * 背景画笔
     */
    private Paint paint;
    /**
     * 当前状态
     */
    private ViewState viewState;
    private ViewState beforeState;
    private ViewState afterState;
    private RectF rect = new RectF();
    /**
     * 动画状态
     */
    private int animateState = ANIMATE_STATE_NONE;
    /**
     *
     */
    private ValueAnimator valueAnimator;
    private final android.animation.ArgbEvaluator argbEvaluator
            = new android.animation.ArgbEvaluator();
    /**
     *是否选中
     */
    private boolean isChecked;
    /**
     * 是否启用动画
     */
    private boolean enableEffect;
    /**
     * 是否启用阴影效果
     */
    private boolean shadowEffect;
    /**
     * 是否显示指示器
     */
    private boolean showIndicator;
    /**
     * 收拾是否按下
     */
    private boolean isTouchingDown = false;
    /**
     *
     */
    private boolean isUiInited = false;
    /**
     *
     */
    private boolean isEventBroadcast = false;
    private OnCheckedChangeListener onCheckedChangeListener;
    /**
     * 手势按下的时刻
     */
    private long touchDownTime;
    private Runnable postPendingDrag = new Runnable() {
        @Override
        public void run() {
            if(!isInAnimating()){
                pendingDragState();
            }
        }
    };
    private ValueAnimator.AnimatorUpdateListener animatorUpdateListener
            = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value = (Float) animation.getAnimatedValue();
            switch (animateState) {
                case ANIMATE_STATE_PENDING_SETTLE: {
                }
                case ANIMATE_STATE_PENDING_RESET: {
                }
                case ANIMATE_STATE_PENDING_DRAG: {
                    viewState.checkedLineColor = (int) argbEvaluator.evaluate(
                            value,
                            beforeState.checkedLineColor,
                            afterState.checkedLineColor
                    );
                    viewState.radius = beforeState.radius
                            + (afterState.radius - beforeState.radius) * value;
                    if(animateState != ANIMATE_STATE_PENDING_DRAG){
                        viewState.buttonX = beforeState.buttonX
                                + (afterState.buttonX - beforeState.buttonX) * value;
                    }
                    viewState.checkStateColor = (int) argbEvaluator.evaluate(
                            value,
                            beforeState.checkStateColor,
                            afterState.checkStateColor
                    );
                    break;
                }
                case ANIMATE_STATE_SWITCH: {
                    viewState.buttonX = beforeState.buttonX
                            + (afterState.buttonX - beforeState.buttonX) * value;
                    float fraction = (viewState.buttonX - buttonMinX) / (buttonMaxX - buttonMinX);
                    viewState.checkStateColor = (int) argbEvaluator.evaluate(
                            fraction,
                            uncheckColor,
                            checkedColor
                    );
                    viewState.radius = fraction * viewRadius;
                    viewState.checkedLineColor = (int) argbEvaluator.evaluate(
                            fraction,
                            Color.TRANSPARENT,
                            checkLineColor
                    );
                    break;
                }
                default:
                case ANIMATE_STATE_DRAGING: {
                }
                case ANIMATE_STATE_NONE: {
                    break;
                }
            }
            postInvalidate();
        }
    };
    private Animator.AnimatorListener animatorListener
            = new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
        }
        @Override
        public void onAnimationEnd(Animator animation) {
            switch (animateState) {
                case ANIMATE_STATE_DRAGING: {
                    break;
                }
                case ANIMATE_STATE_PENDING_DRAG: {
                    animateState = ANIMATE_STATE_DRAGING;
                    viewState.checkedLineColor = Color.TRANSPARENT;
                    viewState.radius = viewRadius;
                    postInvalidate();
                    break;
                }
                case ANIMATE_STATE_PENDING_RESET: {
                    animateState = ANIMATE_STATE_NONE;
                    postInvalidate();
                    break;
                }
                case ANIMATE_STATE_PENDING_SETTLE: {
                    animateState = ANIMATE_STATE_NONE;
                    postInvalidate();
                    broadcastEvent();
                    break;
                }
                case ANIMATE_STATE_SWITCH: {
                    isChecked = !isChecked;
                    animateState = ANIMATE_STATE_NONE;
                    postInvalidate();
                    broadcastEvent();
                    break;
                }
                default:
                case ANIMATE_STATE_NONE: {
                    break;
                }
            }
        }
        @Override
        public void onAnimationCancel(Animator animation) {
        }
        @Override
        public void onAnimationRepeat(Animator animation) {
        }
    };
    /*******************************************************/
    /**
     * 保存动画状态
     * */
    private static class ViewState {
        /**
         * 按钮x位置[buttonMinX-buttonMaxX]
         */
        float buttonX;
        /**
         * 状态背景颜色
         */
        int checkStateColor;
        /**
         * 选中线的颜色
         */
        int checkedLineColor;
        /**
         * 状态背景的半径
         */
        float radius;
        ViewState(){}
        private void copy(ViewState source){
            this.buttonX = source.buttonX;
            this.checkStateColor = source.checkStateColor;
            this.checkedLineColor = source.checkedLineColor;
            this.radius = source.radius;
        }
    }
}


然后是修改activity_background_manager.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:fitsSystemWindows="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.BackgroundManagerActivity">
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/white"
        app:layout_constraintEnd_toEndOf="parent"
        app:navigationIcon="@mipmap/icon_return"
        app:contentInsetLeft="@dimen/dp_16"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:title=""
        app:popupTheme="@style/AppTheme.PopupOverlay">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textSize="@dimen/sp_16"
            android:textColor="@color/black"
            android:text="背景管理" />
    </androidx.appcompat.widget.Toolbar>
    <LinearLayout
        android:orientation="vertical"
        android:layout_marginTop="@dimen/dp_8"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!--每日一图-->
        <LinearLayout
            android:background="@color/white"
            android:paddingLeft="@dimen/dp_16"
            android:paddingRight="@dimen/dp_16"
            android:paddingTop="@dimen/dp_8"
            android:paddingBottom="@dimen/dp_8"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:text="每日一图"
                android:textColor="@color/black"
                android:textSize="@dimen/sp_16"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"/>
            <com.llw.mvplibrary.view.SwitchButton
                android:id="@+id/wb_everyday"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <!--透明分割线-->
        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_1"
            android:background="@color/transparent"/>
        <!--图片列表-->
        <LinearLayout
            android:background="@color/white"
            android:paddingLeft="@dimen/dp_16"
            android:paddingRight="@dimen/dp_16"
            android:paddingTop="@dimen/dp_8"
            android:paddingBottom="@dimen/dp_8"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:text="图片列表"
                android:textColor="@color/black"
                android:textSize="@dimen/sp_16"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"/>
            <com.llw.mvplibrary.view.SwitchButton
                android:id="@+id/wb_img_list"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_1"
            android:background="@color/transparent"/>
        <!--手动定义-->
        <LinearLayout
            android:background="@color/white"
            android:paddingLeft="@dimen/dp_16"
            android:paddingRight="@dimen/dp_16"
            android:paddingTop="@dimen/dp_8"
            android:paddingBottom="@dimen/dp_8"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:text="手动定义"
                android:textColor="@color/black"
                android:textSize="@dimen/sp_16"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"/>
            <com.llw.mvplibrary.view.SwitchButton
                android:id="@+id/wb_custom_"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>


接下来就是BackgroundManagerActivity.java


20200508171027289.png

逻辑思路


从布局中可以看出来有三个开关按钮,但是只能开一个,并且开启这个的同时要关闭其他的按钮,所以这个有一个相互的监听事件,并且监听事件里面要对这个按钮的是否开启做一个结果存储,这里会用到缓存,缓存是在项目全局中适用的,所以可以跨页面,比如我在这个页面里面开启了每日一图的按钮,存储值为true,然后在回到MainActivity中时,取出这个缓存里面true,然后根据这个值启用每日一图,达到更换壁纸的目的,这是最简单的操作,至于本地更换背景就比较麻烦一点,会需要先在项目的drawable里面先放几张图片,然后通过弹窗来展示这个图片,点击某一个背景则切换成功,然后弹窗关闭,这时候你的图片列表对应的按钮才会开启,加入你打开弹窗之后并没有选中任何图片,则关闭弹窗的同时也会关闭这个按钮,最后一个手动定义,就是打开手机里面图库,任你选择图片,选择之后立即提交上去,这里要先进行动态权限的申请。同意之后打开相册,好了,目前是这样的。

然后开始写代码吧

20200508172538274.png


首先创建两个java类

SPUtils.java

package com.llw.goodweather.utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
 * sharepref工具类
 */
public class SPUtils {
    private static final String NAME="config";
    public static void putBoolean(String key, boolean value, Context ctx) {
        SharedPreferences sp = ctx.getSharedPreferences(NAME,
                Context.MODE_PRIVATE);
        sp.edit().putBoolean(key, value).commit();
    }
    public static boolean getBoolean(String key, boolean defValue, Context ctx) {
        SharedPreferences sp = ctx.getSharedPreferences(NAME,
                Context.MODE_PRIVATE);
        return sp.getBoolean(key, defValue);
    }
    public static void putString(String key, String value, Context ctx) {
        SharedPreferences sp = ctx.getSharedPreferences(NAME,
                Context.MODE_PRIVATE);
        sp.edit().putString(key, value).commit();
    }
    public static String getString(String key, String defValue, Context ctx) {
        if(ctx!=null){
            SharedPreferences sp = ctx.getSharedPreferences(NAME,
                    Context.MODE_PRIVATE);
            return sp.getString(key, defValue);
        }
        return "";
    }
    public static void putInt(String key, int value, Context ctx) {
        SharedPreferences sp = ctx.getSharedPreferences(NAME,
                Context.MODE_PRIVATE);
        sp.edit().putInt(key, value).commit();
    }
    public static int getInt(String key, int defValue, Context ctx) {
        SharedPreferences sp = ctx.getSharedPreferences(NAME,
                Context.MODE_PRIVATE);
        return sp.getInt(key, defValue);
    }
    public static void remove(String key, Context ctx) {
        SharedPreferences sp = ctx.getSharedPreferences(NAME,
                Context.MODE_PRIVATE);
        sp.edit().remove(key).commit();
    }
}


Constant.java


package com.llw.goodweather.utils;
/**
 * 统一管理缓存中对应的KEY
 */
public class Constant {
    public final static int SUCCESS_CODE = 200;
    public final static String CITY = "city";//市
    public final static String DISTRICT = "district";//区/县
    public final static String EVERYDAY_IMG = "everyday_img";//每日图片开关
    public final static String IMG_LIST = "img_list";//图片列表开关
    public final static String CUSTOM_IMG = "custom_img";//手动定义开关
}


接下来修改mvplibrary中的BaseActivity.java


private static final int FAST_CLICK_DELAY_TIME = 500;
private static long lastClickTime;
//返回
public void Back(Toolbar toolbar){
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                context.finish();
                if(!isFastClick()) {
                    context.finish();
                }
            }
        });
    }
    // 两次点击间隔不能少于500ms
    public static boolean isFastClick() {
        boolean flag = true;
        long currentClickTime = System.currentTimeMillis();
        if ((currentClickTime - lastClickTime) >= FAST_CLICK_DELAY_TIME ) {
            flag = false;
        }
        lastClickTime = currentClickTime;
        return flag;
    }


在这里写好之后,到时候在Activity中直接调用即可。然后回到BackgroundManagerActivity

2020050817400639.png


继承BaseActivity之后就可以用它里面的一些方法了。

2020050817531074.png


initSwitchButton方法


  //初始化三个开关按钮  三个只能开一个
    private void initSwitchButton() {
        //每日一图按钮开关监听
        wbEveryday.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(SwitchButton view, boolean isChecked) {
                if(isChecked){//开
                    SPUtils.putBoolean(Constant.EVERYDAY_IMG,true,context);
                    wbImgList.setChecked(false);
                    wbCustom.setChecked(false);
                }else {//关
                    SPUtils.putBoolean(Constant.EVERYDAY_IMG,false,context);
                }
            }
        });
        //图片列表按钮开关监听
        wbImgList.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(SwitchButton view, boolean isChecked) {
                if(isChecked){
                    SPUtils.putBoolean(Constant.IMG_LIST,true,context);
                    wbEveryday.setChecked(false);
                    wbCustom.setChecked(false);
                }else {
                    SPUtils.putBoolean(Constant.IMG_LIST,false,context);
                }
            }
        });
        //手动定义按钮开关监听
        wbCustom.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(SwitchButton view, boolean isChecked) {
                if(isChecked){
                    SPUtils.putBoolean(Constant.CUSTOM_IMG,true,context);
                    wbEveryday.setChecked(false);
                    wbImgList.setChecked(false);
                }else {
                    SPUtils.putBoolean(Constant.CUSTOM_IMG,false,context);
                }
            }
        });
    }


然后在MainActivity中指定点击切换背景是页面跳转

20200509104622774.png


现在你就可以运行一下了,一般情况下是不会报错的,运行出来的结果应该是,三个开关按钮,一开始就是默认都没有打开,但是你也只能开一个,开另一个的时候会关闭这个,你可以试一下。

每日一图的比较简单,只要缓存里面放了这个值就行了。

麻烦的是后面这两个


图片列表


首先是六张本地壁纸,放在drawable里,这里我还是贴一下这些壁纸吧

img_1.jpg


20200509110855642.jpg


img_2.jpg

20200509110943757.jpg


img_3.jpg

20200509111010602.jpg


img_4.jpg

20200509111047712.jpg


img_5.jpg

20200509111111477.jpg


img_6.jpg

20200509111132993.jpg


然后创建一个样式文件,用于图片选中后的样式。

现在colors.xml里面增加两个颜色

20200509113648326.png


  <color name="green">#77d034</color><!--绿色-->
    <color name="transparent_bg_white">#22FFFFFF</color><!--白色半透明-->


20200509113840205.png


check_style.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/check_normal_rb" android:state_checked="false"/>
    <item android:drawable="@drawable/check_checked_rb" android:state_checked="true"/>
</selector>


check_normal_rb.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke android:width="1dip" android:color="@color/transparent" />
</shape>


check_checked_rb.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 实心 -->
    <solid android:color="@color/transparent_bg_white" />
    <stroke android:width="2dip" android:color="@color/green" />
</shape>


然后在mvplibrary模块中的build.radle文件中的dependencies{}闭包中增加一个依赖库


20200509141052682.png


  //自由嵌套的RadioGroup
    api 'com.github.fodroid:XRadioGroup:v1.5'


然后Sync一下,否则不会生效的。


然后在app的layout下创建一个新的弹窗布局文件window_img_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="@color/white"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <!--自由嵌套的RadioGroup-->
    <me.shihao.library.XRadioGroup
        android:id="@+id/xrg_img"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <!--线性布局 一横排三个 里面以权重自适应宽度-->
        <LinearLayout
            android:padding="8dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <RelativeLayout
                android:layout_marginRight="@dimen/dp_4"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="200dp">
                <ImageView
                    android:layout_margin="5dp"
                    android:background="@drawable/img_1"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
                <RadioButton
                    android:id="@+id/rb_img_1"
                    android:button="@null"
                    android:background="@drawable/check_style"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </RelativeLayout>
            <RelativeLayout
                android:layout_marginLeft="@dimen/dp_4"
                android:layout_marginRight="@dimen/dp_4"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="200dp">
                <ImageView
                    android:layout_margin="5dp"
                    android:background="@drawable/img_2"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
                <RadioButton
                    android:id="@+id/rb_img_2"
                    android:button="@null"
                    android:background="@drawable/check_style"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </RelativeLayout>
            <RelativeLayout
                android:layout_marginLeft="@dimen/dp_4"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="200dp">
                <ImageView
                    android:layout_margin="5dp"
                    android:background="@drawable/img_3"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
                <RadioButton
                    android:id="@+id/rb_img_3"
                    android:button="@null"
                    android:background="@drawable/check_style"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </RelativeLayout>
        </LinearLayout>
        <!--线性布局 一横排三个 里面以权重自适应宽度-->
        <LinearLayout
            android:padding="8dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <RelativeLayout
                android:layout_marginRight="@dimen/dp_4"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="200dp">
                <ImageView
                    android:layout_margin="5dp"
                    android:background="@drawable/img_4"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
                <RadioButton
                    android:id="@+id/rb_img_4"
                    android:button="@null"
                    android:background="@drawable/check_style"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </RelativeLayout>
            <RelativeLayout
                android:layout_marginLeft="@dimen/dp_4"
                android:layout_marginRight="@dimen/dp_4"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="200dp">
                <ImageView
                    android:layout_margin="5dp"
                    android:background="@drawable/img_5"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
                <RadioButton
                    android:id="@+id/rb_img_5"
                    android:button="@null"
                    android:background="@drawable/check_style"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </RelativeLayout>
            <RelativeLayout
                android:layout_marginLeft="@dimen/dp_4"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="200dp">
                <ImageView
                    android:layout_margin="5dp"
                    android:background="@drawable/img_6"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
                <RadioButton
                    android:id="@+id/rb_img_6"
                    android:button="@null"
                    android:background="@drawable/check_style"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
            </RelativeLayout>
        </LinearLayout>
    </me.shihao.library.XRadioGroup>
</LinearLayout>


然后在BackgroundManagerActivity.java中用弹窗显示

20200509142150337.png


  LiWindow liWindow;//弹窗
    RxPermissions rxPermissions;//权限请求
    private AnimationUtil animationUtil;//动画工具类
    private float bgAlpha = 1f;//背景透明度
    private boolean bright = false;//判断标识
    private static final long DURATION = 500;//0.5s
    private static final float START_ALPHA = 0.7f;//开始透明度
    private static final float END_ALPHA = 1f;//结束透明度


找次的弹窗我打算底部显示出现,里面的动画文件早就已经写好了,在前几篇就有,如果你是一步一步跟著我写过来的,那么就不会有问题,现在主要是修改LiWindow.java中的**showBottomPopupWindow()**方法,修改后的代码如下:


  /**
     * 底部显示
     * @param mView
     */
    public void showBottomPopupWindow(View mView,PopupWindow.OnDismissListener onDismissListener) {
        mPopupWindow = new PopupWindow(mView,
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        mPopupWindow.setContentView(mView);
        mPopupWindow.setOutsideTouchable(true);//点击空白处不关闭弹窗  true为关闭
        mPopupWindow.setFocusable(true);
        mPopupWindow.setAnimationStyle(R.style.AnimationBottomFade); //设置动画
        mPopupWindow.showAtLocation(mView, Gravity.BOTTOM, 0, 0);
        mPopupWindow.setOnDismissListener(onDismissListener);
    }


主要是将里面的窗口关闭方法的监听放在里传入参数里,这样既可以在Activity中操作弹窗关闭时的事件了。

接下来是弹窗的使用

 /**
     * 显示图片弹窗
     */
    private void showImgWindow() {
        liWindow = new LiWindow(context);
        final View view = LayoutInflater.from(context).inflate(R.layout.window_img_list, null);
        XRadioGroup xRadioGroup = (XRadioGroup) view.findViewById(R.id.xrg_img);
        //显示弹窗的时候,取缓存,判断里面有没有选中过图片
        int position = SPUtils.getInt(Constant.IMG_POSITION, -1, context);
        RadioButton rbImg1 = (RadioButton) view.findViewById(R.id.rb_img_1);
        RadioButton rbImg2 = (RadioButton) view.findViewById(R.id.rb_img_2);
        RadioButton rbImg3 = (RadioButton) view.findViewById(R.id.rb_img_3);
        RadioButton rbImg4 = (RadioButton) view.findViewById(R.id.rb_img_4);
        RadioButton rbImg5 = (RadioButton) view.findViewById(R.id.rb_img_5);
        RadioButton rbImg6 = (RadioButton) view.findViewById(R.id.rb_img_6);
        switch (position) {
            case 0:
                rbImg1.setChecked(true);
                break;
            case 1:
                rbImg2.setChecked(true);
                break;
            case 2:
                rbImg3.setChecked(true);
                break;
            case 3:
                rbImg4.setChecked(true);
                break;
            case 4:
                rbImg5.setChecked(true);
                break;
            case 5:
                rbImg6.setChecked(true);
                break;
        }
        //xRadioGroup的选中监听
        xRadioGroup.setOnCheckedChangeListener(new XRadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(XRadioGroup xRadioGroup, int i) {
                //得出选中id对应的RadioButton,从而知道选中的是哪一个,并放入缓存,0~5 
                switch (xRadioGroup.getCheckedRadioButtonId()) {
                    case R.id.rb_img_1:
                        SPUtils.putInt(Constant.IMG_POSITION, 0, context);
                        liWindow.closePopupWindow();
                        break;
                    case R.id.rb_img_2:
                        SPUtils.putInt(Constant.IMG_POSITION, 1, context);
                        liWindow.closePopupWindow();
                        break;
                    case R.id.rb_img_3:
                        SPUtils.putInt(Constant.IMG_POSITION, 2, context);
                        liWindow.closePopupWindow();
                        break;
                    case R.id.rb_img_4:
                        SPUtils.putInt(Constant.IMG_POSITION, 3, context);
                        liWindow.closePopupWindow();
                        break;
                    case R.id.rb_img_5:
                        SPUtils.putInt(Constant.IMG_POSITION, 4, context);
                        liWindow.closePopupWindow();
                        break;
                    case R.id.rb_img_6:
                        SPUtils.putInt(Constant.IMG_POSITION, 5, context);
                        liWindow.closePopupWindow();
                        break;
                    default:
                        SPUtils.putInt(Constant.IMG_POSITION, 5, context);
                        break;
                }
                ToastUtils.showShortToast(context,"已更换壁纸");
            }
        });
        //弹窗关闭监听  弹窗关闭时,如果什么都没有选中,则自然不会有缓存中的取会0~5,所以当为-1时,关闭图片列表的开关
        PopupWindow.OnDismissListener onDismissListener = new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                toggleBright();
                int position = SPUtils.getInt(Constant.IMG_POSITION, -1, context);
                if (position != -1) {
                    wbImgList.setChecked(true);
                } else {
                    wbImgList.setChecked(false);
                }
            }
        };
        liWindow.showBottomPopupWindow(view, onDismissListener);//显示弹窗 ,传入关闭弹窗监听
    }
    /**
     * 计算动画时间
     */
    private void toggleBright() {
        // 三个参数分别为:起始值 结束值 时长,那么整个动画回调过来的值就是从0.5f--1f的
        animationUtil.setValueAnimator(START_ALPHA, END_ALPHA, DURATION);
        animationUtil.addUpdateListener(new AnimationUtil.UpdateListener() {
            @Override
            public void progress(float progress) {
                // 此处系统会根据上述三个值,计算每次回调的值是多少,我们根据这个值来改变透明度
                bgAlpha = bright ? progress : (START_ALPHA + END_ALPHA - progress);
                backgroundAlpha(bgAlpha);
            }
        });
        animationUtil.addEndListner(new AnimationUtil.EndListener() {
            @Override
            public void endUpdate(Animator animator) {
                // 在一次动画结束的时候,翻转状态
                bright = !bright;
            }
        });
        animationUtil.startAnimator();
    }
    /**
     * 此方法用于改变背景的透明度,从而达到“变暗”的效果
     */
    private void backgroundAlpha(float bgAlpha) {
        WindowManager.LayoutParams lp = getWindow().getAttributes();
        // 0.0-1.0
        lp.alpha = bgAlpha;
        getWindow().setAttributes(lp);
        // everything behind this window will be dimmed.
        // 此方法用来设置浮动层,防止部分手机变暗无效
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    }

注释都写在代码里面了,可能一开始你会看到云里雾里,不过我相信你会明白的,接下来就是这个弹窗的显示调用


20200509143522986.png


到这里图片列表就写完了。你不妨运行一下


手动定义


在Constant.java

20200509144227400.png


红框中为新增的Key

20200509143956951.png


下面的内容会用到一个相机相册的工具类,代码我贴一下:


package com.llw.goodweather.utils;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.ImageView;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
/**
 * 相机、相册工具类
 */
public class CameraUtils {
    public static Intent getTakePhotoIntent(Context context, File outputImagepath){
        //获取系統版本
        int currentapiVersion = Build.VERSION.SDK_INT;
        // 激活相机
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 判断存储卡是否可以用,可用进行存储
        if (hasSdcard()) {
            if (currentapiVersion < 24) {
                // 从文件中创建uri
                Uri uri = Uri.fromFile(outputImagepath);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            } else {
                //兼容android7.0 使用共享文件的形式
                ContentValues contentValues = new ContentValues(1);
                contentValues.put(MediaStore.Images.Media.DATA, outputImagepath.getAbsolutePath());
                Uri uri = context.getApplicationContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            }
        }
        return intent;
    }
    public static Intent getSelectPhotoIntent(){
        Intent intent = new Intent("android.intent.action.GET_CONTENT");
        intent.setType("image/*");
        return intent;
    }
    /*
     * 判断sdcard是否被挂载
     */
    public static boolean hasSdcard() {
        return Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED);
    }
    /**
     * 4.4及以上系统处理图片的方法
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String getImgeOnKitKatPath(Intent data, Context context) {
        String imagePath = null;
        Uri uri = data.getData();
        Log.d("uri=intent.getData :", "" + uri);
        if (DocumentsContract.isDocumentUri(context, uri)) {
            String docId = DocumentsContract.getDocumentId(uri);        //数据表里指定的行
            Log.d("getDocumentId(uri) :", "" + docId);
            Log.d("uri.getAuthority() :", "" + uri.getAuthority());
            if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
                String id = docId.split(":")[1];
                String selection = MediaStore.Images.Media._ID + "=" + id;
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection,context);
            } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
                imagePath = getImagePath(contentUri, null,context);
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            imagePath = getImagePath(uri, null,context);
        }
        return imagePath;
    }
    /**
     * 通过uri和selection来获取真实的图片路径,从相册获取图片时要用
     */
    public static String getImagePath(Uri uri, String selection, Context context) {
        String path = null;
        Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null);
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }
            cursor.close();
        }
        return path;
    }
    //改变拍完照后图片方向不正的问题
    public static void ImgUpdateDirection(String filepath, Bitmap orc_bitmap, ImageView iv) {
        int digree = 0;//图片旋转的角度
        //根据图片的URI获取图片的绝对路径
        Log.i("tag", ">>>>>>>>>>>>>开始");
        //String filepath = ImgUriDoString.getRealFilePath(getApplicationContext(), uri);
        Log.i("tag", "》》》》》》》》》》》》》》》" + filepath);
        //根据图片的filepath获取到一个ExifInterface的对象
        ExifInterface exif = null;
        try {
            exif = new ExifInterface(filepath);
            Log.i("tag", "exif》》》》》》》》》》》》》》》" + exif);
            if (exif != null) {
                // 读取图片中相机方向信息
                int ori = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
                // 计算旋转角度
                switch (ori) {
                    case ExifInterface.ORIENTATION_ROTATE_90:
                        digree = 90;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_180:
                        digree = 180;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_270:
                        digree = 270;
                        break;
                    default:
                        digree = 0;
                        break;
                }
            }
            //如果图片不为0
            if (digree != 0) {
                // 旋转图片
                Matrix m = new Matrix();
                m.postRotate(digree);
                orc_bitmap = Bitmap.createBitmap(orc_bitmap, 0, 0, orc_bitmap.getWidth(),
                        orc_bitmap.getHeight(), m, true);
            }
            if (orc_bitmap != null) {
                iv.setImageBitmap(orc_bitmap);
            }
        } catch (IOException e) {
            e.printStackTrace();
            exif = null;
        }
    }
    /**
     * 4.4以下系统处理图片的方法
     */
    public static String getImageBeforeKitKatPath(Intent data, Context context) {
        Uri uri = data.getData();
        String imagePath = getImagePath(uri, null,context);
        return imagePath;
    }
    //比例压缩
    public static Bitmap comp(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        if (baos.toByteArray().length / 1024 > 5120) {//判断如果图片大于5M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        //开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 800f;//这里设置高度为800f
        float ww = 480f;//这里设置宽度为480f
        //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;//be=1表示不缩放
        if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;//设置缩放比例
        newOpts.inPreferredConfig = Bitmap.Config.RGB_565;//降低图片从ARGB888到RGB565
        //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        isBm = new ByteArrayInputStream(baos.toByteArray());
        bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        return bitmap;//压缩好比例大小后再进行质量压缩
    }
}


手动定义,顾名思义,就是打开本地相册,所以首先得版本判断,然后是权限请求,打开相册,返回图片的路径。


  //权限判断
    private void permissionVersion() {
        Intent intent = new Intent();
        if (Build.VERSION.SDK_INT >= 23) {//6.0或6.0以上
            //动态权限申请
            permissionsRequest();
        } else {//6.0以下
            //发现只要权限在AndroidManifest.xml中注册过,均会认为该权限granted  提示一下即可
            if (Build.VERSION.SDK_INT <19) {
                intent.setAction(Intent.ACTION_GET_CONTENT);
            }else {
                intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
            }
            startActivityForResult(intent, SELECT_PHOTO);
        }
    }
    //动态权限申请
    private void permissionsRequest() {//使用这个框架需要制定JDK版本,建议用1.8
        rxPermissions = new RxPermissions(context);
        rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                .subscribe(granted -> {
                    if (granted) {//申请成功
                        //得到权限之后打开本地相册
                        Intent selectPhotoIntent = CameraUtils.getSelectPhotoIntent();
                        startActivityForResult(selectPhotoIntent, SELECT_PHOTO);
                    } else {//申请失败
                        wbCustom.setChecked(false);
                        ToastUtils.showShortToast(this, "权限未开启");
                    }
                });
    }


返回结果中获取图片地址,然后放入缓存中


  /**
     * Activity返回结果
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            //打开相册后返回
            case SELECT_PHOTO:
                if (resultCode == RESULT_OK) {
                    String imagePath = null;
                    //判断手机系统版本号
                    if (Build.VERSION.SDK_INT > 19) {
                        //4.4及以上系统使用这个方法处理图片
                        imagePath = CameraUtils.getImgeOnKitKatPath(data, this);
                    } else {
                        imagePath = CameraUtils.getImageBeforeKitKatPath(data, this);
                    }
                    displayImage(imagePath);
                }
                break;
            default:
                break;
        }
    }
    /**
     * 从相册获取完图片(根据图片路径显示图片)
     */
    private void displayImage(String imagePath) {
        if (!TextUtils.isEmpty(imagePath)) {
            //将本地上传选中的图片地址放入缓存,当手动定义开关打开时,取出缓存中的图片地址,显示为背景
            SPUtils.putString(Constant.CUSTOM_IMG_PATH, imagePath, context);
            ToastUtils.showShortToast(context,"已更换为你选择的壁纸");
        } else {
            wbCustom.setChecked(true);//关闭手动定义开关
            ToastUtils.showShortToast(context,"图片获取失败");
        }
    }

20200509153118789.png

20200509153546518.png

    boolean isEverydayImg = SPUtils.getBoolean(Constant.EVERYDAY_IMG, false, context);//每日图片
        boolean isImgList = SPUtils.getBoolean(Constant.IMG_LIST, false, context);//图片列表
        boolean isCustomImg = SPUtils.getBoolean(Constant.CUSTOM_IMG, false, context);//手动定义
        if (isEverydayImg == true) {
            wbEveryday.setChecked(true);
        } else if (isImgList == true) {
            wbImgList.setChecked(true);
        } else if (isCustomImg == true) {
            wbCustom.setChecked(true);
        }


完成这一步之后,这个BackgroundManagerActivity.java的代码就写完了,但具体的功能还没有完成,因为MainActivity才是显示你这个背景的地方啊。


回来MainActivity.java


这里要聊一下关于Actiivty的生命周期的问题,但是鉴于本篇博客的内容和字数已经够多了,所以我放一个链接Activity生命周期,你可以去看看这个生命周期,否则我后面的内容你就不是很能理解为什么。


20200509145815850.png


然后在跳转壁纸管理页面的时候增加当前页面区/县、市的缓存,当在回到MainActivity时,取出缓存中的值,然后进行接口请求。

20200509153753719.png


  @Override
    protected void onResume() {
        super.onResume();
        showLoadingDialog();//在数据请求之前放在加载等待弹窗,返回结果后关闭弹窗
        if(district == null){
            //取出缓存
            district = SPUtils.getString(Constant.DISTRICT,"",context);
            city = SPUtils.getString(Constant.CITY,"",context);
        }
        isOpenChangeBg();//是否开启了切换背景
        //获取今天的天气数据
        mPresent.todayWeather(context, district);
        //获取天气预报数据
        mPresent.weatherForecast(context, district);
        //获取生活指数数据
        mPresent.lifeStyle(context, district);
        //获取逐小时天气数据
        mPresent.hourly(context, district);
        //获取空气质量数据
        mPresent.airNowCity(context, city);
    }
    //判断是否开启了切换背景,没有开启则用默认的背景
    private void isOpenChangeBg() {
        boolean isEverydayImg = SPUtils.getBoolean(Constant.EVERYDAY_IMG,false,context);//每日图片
        boolean isImgList = SPUtils.getBoolean(Constant.IMG_LIST,false,context);//图片列表
        boolean isCustomImg = SPUtils.getBoolean(Constant.CUSTOM_IMG,false,context);//手动定义
        //因为只有有一个为true,其他两个就都会是false,所以可以一个一个的判断
        if(isEverydayImg != true && isImgList != true && isCustomImg != true){
            //当所有开关都没有打开的时候用默认的图片
            bg.setBackgroundResource(R.drawable.pic_bg);
        }else {
            if(isEverydayImg!=false){//开启每日一图
                mPresent.biying(context);
            }else if(isImgList!=false){//开启图片列表
                int position = SPUtils.getInt(Constant.IMG_POSITION,-1,context);
                switch (position){
                    case 0:
                        bg.setBackgroundResource(R.drawable.img_1);
                        break;
                    case 1:
                        bg.setBackgroundResource(R.drawable.img_2);
                        break;
                    case 2:
                        bg.setBackgroundResource(R.drawable.img_3);
                        break;
                    case 3:
                        bg.setBackgroundResource(R.drawable.img_4);
                        break;
                    case 4:
                        bg.setBackgroundResource(R.drawable.img_5);
                        break;
                    case 5:
                        bg.setBackgroundResource(R.drawable.img_6);
                        break;
                }
            }else if(isCustomImg != false){
                String imgPath = SPUtils.getString(Constant.CUSTOM_IMG_PATH,"",context);
                Glide.with(context)
                        .asBitmap()
                        .load(imgPath)
                        .into(new SimpleTarget<Bitmap>() {
                            @Override
                            public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
                                Drawable drawable = new BitmapDrawable(context.getResources(), resource);
                                bg.setBackground(drawable);
                            }
                        });
            }
        }
    }


到这一步,页面就算是真正的写完了。说实话写博客是一个耗费脑力和体力的活,思路很重要啊。


相关文章
|
8天前
|
数据采集 网络协议 算法
移动端弱网优化专题(十四):携程APP移动网络优化实践(弱网识别篇)
本文从方案设计、代码开发到技术落地,详尽的分享了携程在移动端弱网识别方面的实践经验,如果你也有类似需求,这篇文章会是一个不错的实操指南。
21 1
|
2月前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
64 20
Android经典面试题之图片Bitmap怎么做优化
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
46 4
|
30天前
|
小程序
微信小程序动态tabBar实现:基于自定义组件,灵活支持不同用户角色与超过5个tab自由组合(更新版)
微信小程序动态tabBar实现:基于自定义组件,灵活支持不同用户角色与超过5个tab自由组合(更新版)
397 1
|
1月前
|
小程序 搜索推荐 API
微信小程序:自定义关注公众号组件样式
尽管关注公众号组件的样式固定且不可修改,但产品经理的需求却需要个性化的定制。在这种情况下,我们需要寻找解决方案,以满足这些特殊需求,尽管这可能有点棘手。
59 0
微信小程序:自定义关注公众号组件样式
|
14天前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。
|
3月前
|
小程序 开发者
Taro@3.x+Vue@3.x+TS开发微信小程序,使用自定义tabBar
本文介绍了如何在Taro项目中实现自定义tabBar。首先,在`app.config.ts`中设置`custom: true`并配置`tabBar`。
127 0
Taro@3.x+Vue@3.x+TS开发微信小程序,使用自定义tabBar
|
2月前
|
Java Android开发 UED
安卓应用开发中的内存管理优化技巧
在安卓开发的广阔天地里,内存管理是一块让开发者既爱又恨的领域。它如同一位严苛的考官,时刻考验着开发者的智慧与耐心。然而,只要我们掌握了正确的优化技巧,就能够驯服这位考官,让我们的应用在性能和用户体验上更上一层楼。本文将带你走进内存管理的迷宫,用通俗易懂的语言解读那些看似复杂的优化策略,让你的开发之路更加顺畅。
60 2
|
2月前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
2月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
70 0

热门文章

最新文章