├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── Readme.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ └── output.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── java │ │ └── wengjiayi │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── java │ │ │ └── wengjiayi │ │ │ ├── ItemNewsActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MyWebViewClient.java │ │ │ ├── NetworkAvail.java │ │ │ ├── ShowDescriptionActivity.java │ │ │ ├── WebContent.java │ │ │ └── rss │ │ │ ├── RssFeed.java │ │ │ ├── RssFeed_SAXParser.java │ │ │ ├── RssHandler.java │ │ │ └── RssItem.java │ ├── res │ │ ├── drawable-v24 │ │ │ ├── ic_launcher_foreground.xml │ │ │ ├── ic_menu_camera.xml │ │ │ ├── ic_menu_gallery.xml │ │ │ ├── ic_menu_manage.xml │ │ │ ├── ic_menu_send.xml │ │ │ ├── ic_menu_share.xml │ │ │ ├── ic_menu_slideshow.xml │ │ │ └── side_nav_bar.xml │ │ ├── drawable │ │ │ ├── ic_launcher_background.xml │ │ │ ├── icon.png │ │ │ └── icon2.png │ │ ├── layout │ │ │ ├── activity_customize.xml │ │ │ ├── activity_itemnews.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_newstop_content.xml │ │ │ ├── activity_show_description.xml │ │ │ ├── app_bar_main.xml │ │ │ ├── checkbox.xml │ │ │ ├── content.xml │ │ │ ├── content_main.xml │ │ │ └── nav_header_main.xml │ │ ├── menu │ │ │ ├── activity_main_drawer.xml │ │ │ ├── item.xml │ │ │ └── main.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── values-v21 │ │ │ └── styles.xml │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ └── web_hi_res_512.png │ └── test │ └── java │ └── com │ └── java │ └── wengjiayi │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pic ├── Screenshot_20180915-120413.jpg ├── Screenshot_20180915-120423.jpg ├── Screenshot_20180915-120439.jpg └── Screenshot_20180915-120513.jpg ├── release ├── app-release.apk └── output.json ├── settings.gradle └── 大作业报告.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | 45 | # Keystore files 46 | # Uncomment the following line if you do not want to check your keystore files in. 47 | #*.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Trinkle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Simple News 2 | 3 | Implement with SDK24, Android version>=7.0 4 | 5 | ## Features 6 | 7 | - RSS 8 | - Choose different topic of news 9 | - Favor news 10 | - Search news 11 | - Share news to other app 12 | - Recommend news by your recent browse 13 | 14 | ## Screenshots 15 | 16 |
17 | 18 | 19 | 20 | 21 |
22 | 23 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.java.wengjiayi" 7 | minSdkVersion 24 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:28.0.0-rc02' 24 | implementation 'com.android.support:support-v4:28.0.0-rc02' 25 | implementation 'com.android.support:design:28.0.0-rc02' 26 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 27 | testImplementation 'junit:junit:4.12' 28 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 29 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 30 | compile 'com.jwenfeng.pulltorefresh:library:1.0.3' 31 | // compile project(':library') 32 | } 33 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/release/output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] -------------------------------------------------------------------------------- /app/src/androidTest/java/com/java/wengjiayi/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.java.wengjiayi", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/ItemNewsActivity.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi; 2 | 3 | import android.content.Context; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.content.Intent; 7 | import android.util.Log; 8 | import android.view.KeyEvent; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.webkit.WebChromeClient; 13 | import android.webkit.WebSettings; 14 | import android.webkit.WebView; 15 | import android.webkit.WebViewClient; 16 | import android.widget.ProgressBar; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import java.io.BufferedReader; 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.io.FileNotFoundException; 25 | import java.io.FileOutputStream; 26 | import java.io.FileWriter; 27 | import java.io.IOException; 28 | import java.io.InputStreamReader; 29 | import java.net.MalformedURLException; 30 | import java.net.URL; 31 | 32 | public class ItemNewsActivity extends AppCompatActivity { 33 | 34 | public String webUrl; 35 | public String favorInfo; 36 | public String fileName; 37 | public String favorFile; 38 | public String prefix; 39 | private WebView webView; 40 | public String html; 41 | public int networkState; 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_newstop_content); 46 | Intent intent = getIntent(); 47 | prefix = getFilesDir().getPath(); 48 | webUrl = intent.getStringExtra("link"); 49 | favorInfo = intent.getStringExtra("info"); 50 | fileName = intent.getStringExtra("file"); 51 | favorFile = prefix + "/favor/" + fileName; 52 | setupViews(); 53 | showContent(); 54 | } 55 | 56 | private void setupViews() { 57 | // scrollView = (ScrollView) findViewById(R.id.news_scrollView); 58 | // newsContentView = (NewsContentView) findViewById(R.id.newscontentView); 59 | webView = (WebView) findViewById(R.id.news_webView); 60 | webView.getSettings().setDomStorageEnabled(true); 61 | webView.getSettings().setJavaScriptEnabled(true); 62 | webView.getSettings().setAppCacheEnabled(true); 63 | webView.getSettings().setSupportZoom(true); 64 | webView.getSettings().setBuiltInZoomControls(true); 65 | webView.setWebChromeClient(new WebChromeClient()); 66 | webView.setWebViewClient(new MyWebViewClient("http://world.people.com.cn")); 67 | webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); 68 | webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); 69 | webView.getSettings().setLoadWithOverviewMode(true); 70 | } 71 | @Override 72 | public boolean onCreateOptionsMenu(Menu menu) { 73 | // Inflate the menu; this adds items to the action bar if it is present. 74 | getMenuInflater().inflate(R.menu.item, menu); 75 | MenuItem tv = (MenuItem) menu.findItem(R.id.action_favor); 76 | if (new File(favorFile).exists()) 77 | tv.setTitle("取消收藏"); 78 | else 79 | tv.setTitle("收藏新闻"); 80 | return true; 81 | } 82 | @Override 83 | public boolean onOptionsItemSelected(MenuItem item) { 84 | // Handle action bar item clicks here. The action bar will 85 | // automatically handle clicks on the Home/Up button, so long 86 | // as you specify a parent activity in AndroidManifest.xml. 87 | int id = item.getItemId(); 88 | 89 | //noinspection SimplifiableIfStatement 90 | if (id == R.id.action_share) { 91 | Intent share = new Intent(); 92 | share.setAction(Intent.ACTION_SEND); 93 | share.setType("text/plain"); 94 | // share.putExtra(Intent.EXTRA_SUBJECT, "233"); 95 | share.putExtra(Intent.EXTRA_TEXT, WebContent.getTitle(html).replace(" ","")+" "+webUrl); 96 | share = Intent.createChooser(share, "分享该新闻至:"); 97 | startActivity(share); 98 | return true; 99 | } 100 | if (id == R.id.action_img) { 101 | Toast.makeText(this, "Sorry,暂时不能分享图片", Toast.LENGTH_SHORT).show(); 102 | return true; 103 | } 104 | if (id == R.id.action_clear) { 105 | Toast.makeText(this, "缓存清空成功!", Toast.LENGTH_SHORT).show(); 106 | return true; 107 | } 108 | if (id == R.id.action_favor) { 109 | File favor = new File(favorFile); 110 | Log.i("favor", " " + favor.exists() + " " + favorInfo); 111 | if (favor.exists()) { 112 | favor.delete(); 113 | Toast.makeText(this, "取消收藏成功!", Toast.LENGTH_SHORT).show(); 114 | item.setTitle("收藏新闻"); 115 | } else { 116 | try { 117 | FileWriter fw = new FileWriter(favor); 118 | fw.write(favorInfo); 119 | fw.close(); 120 | // FileOutputStream out = openFileOutput(favorFile, MODE_PRIVATE); 121 | // out.write(favorInfo.getBytes()); 122 | // out.close(); 123 | } catch (FileNotFoundException e) { 124 | e.printStackTrace(); 125 | } catch (IOException e) { 126 | e.printStackTrace(); 127 | } 128 | Toast.makeText(this, "收藏成功!", Toast.LENGTH_SHORT).show(); 129 | item.setTitle("取消收藏"); 130 | } 131 | } 132 | return super.onOptionsItemSelected(item); 133 | } 134 | 135 | private void showContent() { 136 | if (webUrl.contains("qq.com")) 137 | webUrl = webUrl.replace("http", "https"); 138 | Log.i("loadurl", webUrl); 139 | File file = new File(prefix+"/"+fileName); 140 | Log.i("loadurl", prefix+"/"+fileName); 141 | networkState = 1; 142 | Thread t = new Thread(new Runnable() { 143 | @Override 144 | public void run() { 145 | try { 146 | html = WebContent.getHtml(webUrl); 147 | } catch (ExceptionInInitializerError e) { 148 | networkState = 0; 149 | } 150 | } 151 | }); 152 | t.start(); 153 | try{ 154 | t.join(); 155 | } 156 | catch (InterruptedException e) { 157 | e.printStackTrace(); 158 | } 159 | if (!file.exists() || networkState == 1) { 160 | Log.i("state", "no exist " + networkState); 161 | if (networkState == 1) { 162 | String charSet = ""; 163 | try { 164 | charSet = WebContent.getCharset(html); 165 | } catch (ExceptionInInitializerError e) { 166 | ShowDescriptionActivity.show("网络断掉了qwq", this, false); 167 | } 168 | webView.loadDataWithBaseURL(null, html, "text/html", charSet, null); 169 | webView.loadUrl(webUrl); 170 | } 171 | else 172 | html = "网络断掉了qwq

网络断掉了qwq

请检查网络连接。
"; 173 | // ShowDescriptionActivity.show("网络断掉了qwq", this, false); 174 | if (networkState == 1) { 175 | Log.i("html", html); 176 | try { 177 | FileOutputStream out = openFileOutput(fileName, MODE_PRIVATE); 178 | out.write(html.getBytes()); 179 | out.close(); 180 | } catch (FileNotFoundException e) { 181 | e.printStackTrace(); 182 | } catch (IOException e) { 183 | e.printStackTrace(); 184 | } 185 | } 186 | else { 187 | webView.loadDataWithBaseURL(null, html, "text/html", WebContent.getCharset(html), null); 188 | } 189 | } 190 | else { 191 | Log.i("state", "exist"); 192 | try { 193 | FileInputStream inStream=this.openFileInput(fileName); 194 | ByteArrayOutputStream stream=new ByteArrayOutputStream(); 195 | byte[] buffer=new byte[1024]; 196 | int length=-1; 197 | while((length=inStream.read(buffer))!=-1) { 198 | stream.write(buffer,0,length); 199 | } 200 | stream.close(); 201 | inStream.close(); 202 | html = stream.toString(); 203 | } catch (FileNotFoundException e) { 204 | e.printStackTrace(); 205 | } 206 | catch (IOException e){ 207 | e.printStackTrace(); 208 | } 209 | String charSet = ""; 210 | try { 211 | charSet = WebContent.getCharset(html); 212 | } catch (ExceptionInInitializerError e) { 213 | ShowDescriptionActivity.show("网络断掉了qwq", this, false); 214 | } 215 | webView.loadDataWithBaseURL(null, html, "text/html", charSet, null); 216 | } 217 | } 218 | 219 | //点击backspace可返回上个页面,而不是退出(若webview只加载了一个页面) 220 | @Override 221 | public boolean onKeyDown(int keyCode, KeyEvent event) { 222 | if (keyCode == KeyEvent.KEYCODE_BACK) { 223 | if (webView.getVisibility() == View.VISIBLE) { 224 | // 按返回时,看网页是否能返回 225 | if (webView.canGoBack()) { 226 | webView.goBack(); 227 | //返回true webview自己处理 228 | return true; 229 | } 230 | } 231 | } 232 | return super.onKeyDown(keyCode, event); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.FileReader; 10 | import java.io.FileWriter; 11 | import java.io.IOException; 12 | import java.net.UnknownHostException; 13 | import java.text.SimpleDateFormat; 14 | import java.util.ArrayList; 15 | import java.util.Date; 16 | import java.util.Scanner; 17 | 18 | import javax.xml.parsers.ParserConfigurationException; 19 | 20 | import org.xml.sax.SAXException; 21 | 22 | //import com.handmark.pulltorefresh.library.PullToRefreshBase; 23 | //import com.handmark.pulltorefresh.library.PullToRefreshListView; 24 | import com.java.wengjiayi.rss.*; 25 | import com.jwenfeng.library.pulltorefresh.BaseRefreshListener; 26 | import com.jwenfeng.library.pulltorefresh.PullToRefreshLayout; 27 | 28 | import android.app.AlertDialog; 29 | import android.content.Context; 30 | import android.content.DialogInterface; 31 | import android.graphics.Color; 32 | import android.os.AsyncTask; 33 | import android.os.Bundle; 34 | import android.app.Activity; 35 | import android.content.Intent; 36 | import android.os.Handler; 37 | import android.support.annotation.Nullable; 38 | import android.support.v4.view.GravityCompat; 39 | import android.support.v4.widget.DrawerLayout; 40 | import android.support.v7.app.ActionBarDrawerToggle; 41 | import android.support.v7.app.AppCompatActivity; 42 | import android.support.design.widget.FloatingActionButton; 43 | import android.support.design.widget.Snackbar; 44 | import android.support.design.widget.NavigationView; 45 | import android.support.v7.widget.Toolbar; 46 | import android.view.View; 47 | import android.util.AttributeSet; 48 | import android.util.Log; 49 | import android.view.LayoutInflater; 50 | import android.view.Menu; 51 | import android.view.MenuItem; 52 | import android.view.View; 53 | import android.widget.AdapterView; 54 | import android.widget.AdapterView.OnItemClickListener; 55 | import android.widget.ArrayAdapter; 56 | import android.widget.EditText; 57 | import android.widget.LinearLayout; 58 | import android.widget.ListView; 59 | import android.widget.SimpleAdapter; 60 | import android.support.v4.app.NavUtils; 61 | import android.widget.TextView; 62 | import android.widget.Toast; 63 | 64 | public class MainActivity extends AppCompatActivity implements OnItemClickListener{//, NavigationView.OnNavigationItemSelectedListener { 65 | 66 | // 从网络获取RSS地址 67 | public String RSS_URL = "http://news.qq.com/newssh/rss_newssh.xml"; 68 | public String RSS_URL2 = "http://news.qq.com/newssh/rss_newssh.xml"; 69 | public String prefix = ""; 70 | public final String tag = "RSSReader"; 71 | public String[] urls = {"http://www.people.com.cn/rss/politics.xml", 72 | "http://www.people.com.cn/rss/world.xml", 73 | "http://www.people.com.cn/rss/finance.xml", 74 | "http://www.people.com.cn/rss/sports.xml", 75 | "http://www.people.com.cn/rss/haixia.xml", 76 | "http://www.people.com.cn/rss/edu.xml", 77 | "http://www.people.com.cn/rss/culture.xml", 78 | "http://www.people.com.cn/rss/game.xml"}; 79 | private RssFeed feed = null, feed2 = null; 80 | private ListView newsList; 81 | private SimpleAdapter simpleAdapter; 82 | boolean[] choiceSets = {true,true,true,true,true,true,true,true}; 83 | int[] count = {0,0,0,0,0,0,0,0}; 84 | Context context; 85 | public void getBest() { 86 | int id = 0, id2 = 0, max = 0, max2 = 0; 87 | for (int i = 0; i < count.length; ++i) { 88 | if (max < count[i]) { 89 | id2 = id; 90 | max2 = max; 91 | id = i; 92 | max = count[i]; 93 | } else if (count[i] <= max && max2 < count[i]) { 94 | id2 = i; 95 | max2 = count[i]; 96 | } 97 | } 98 | RSS_URL = urls[id]; 99 | RSS_URL2 = urls[id2]; 100 | Log.i("best", RSS_URL+" "+RSS_URL2); 101 | Log.i("best", id + " " + id2); 102 | Log.i("best", " "+count[0]+" "+count[1]+" "+count[2]+" "+count[3]+" "+count[4]+" "+count[5]+" "+count[6]+" "+count[7]); 103 | } 104 | @Override 105 | public void onCreate(Bundle savedInstanceState) { 106 | super.onCreate(savedInstanceState); 107 | setContentView(R.layout.activity_main); 108 | context = this; 109 | // 获取控件 110 | RSS_URL = "http://www.people.com.cn/rss/world.xml"; 111 | // TextView tv = (TextView)findViewById(R.id.title); 112 | // tv.setText("国际新闻"); 113 | prefix = getFilesDir().getPath(); 114 | Log.i("favorDir", prefix+"/favor"); 115 | File favor = new File(prefix+"/favor"); 116 | if (!favor.exists()) favor.mkdirs(); 117 | File file = new File(prefix+"/9bf6d3581229"); 118 | String state = "11111111"; 119 | if (file.exists()) { 120 | try { 121 | FileInputStream inStream=this.openFileInput("9bf6d3581229"); 122 | ByteArrayOutputStream stream=new ByteArrayOutputStream(); 123 | byte[] buffer=new byte[8]; 124 | int length=-1; 125 | if((length=inStream.read(buffer))!=-1) 126 | stream.write(buffer,0,length); 127 | stream.close(); 128 | inStream.close(); 129 | state = stream.toString(); 130 | } catch (FileNotFoundException e) { 131 | e.printStackTrace(); 132 | } 133 | catch (IOException e){ 134 | e.printStackTrace(); 135 | } 136 | } 137 | File rec = new File(prefix+"/rec.txt"); 138 | if (rec.exists()) { 139 | try { 140 | Scanner scan = new Scanner(rec); 141 | for (int i = 0; i < 8; ++i) 142 | count[i] = scan.nextInt(); 143 | } catch (FileNotFoundException e) { 144 | e.printStackTrace(); 145 | } 146 | } 147 | getBest(); 148 | Log.i("init", state); 149 | setUpColumn(R.id.col_, "recommend"); 150 | setUpColumn(R.id.col0, "favor"); 151 | setUpColumn(R.id.col1, urls[0]); 152 | setUpColumn(R.id.col2, urls[1]); 153 | setUpColumn(R.id.col3, urls[2]); 154 | setUpColumn(R.id.col4, urls[3]); 155 | setUpColumn(R.id.col5, urls[4]); 156 | setUpColumn(R.id.col6, urls[5]); 157 | setUpColumn(R.id.col7, urls[6]); 158 | setUpColumn(R.id.col8, urls[7]); 159 | setState(state); 160 | setPullToRefesh(); 161 | refreshMain(); 162 | } 163 | private int findUrl(String s) { 164 | for (int i = 0; i < urls.length; ++i) 165 | if (urls[i].equals(s)) 166 | return i; 167 | return -1; 168 | } 169 | private void setState(String state) { 170 | TextView tv; 171 | Log.i("state", state); 172 | for (int i = 0; i < urls.length; ++i) { 173 | int id; 174 | if (i == 0) id = R.id.col1; 175 | else if (i == 1) id = R.id.col2; 176 | else if (i == 2) id = R.id.col3; 177 | else if (i == 3) id = R.id.col4; 178 | else if (i == 4) id = R.id.col5; 179 | else if (i == 5) id = R.id.col6; 180 | else if (i == 6) id = R.id.col7; 181 | else id = R.id.col8; 182 | tv = (TextView) findViewById(id); 183 | if (state.charAt(i) == '1') { 184 | tv.setVisibility(View.VISIBLE); 185 | Log.i("state", "true"); 186 | choiceSets[i] = true; 187 | } 188 | else { 189 | tv.setVisibility(View.GONE); 190 | Log.i("state", "false"); 191 | choiceSets[i] = false; 192 | } 193 | } 194 | } 195 | private void refreshMain() { 196 | Log.i("rssurl:", RSS_URL); 197 | if (!NetworkAvail.check(this)) { 198 | ShowDescriptionActivity.show("没有网络qwq请检查", this, true); 199 | Log.i("network", "no"); 200 | return; 201 | } 202 | if (RSS_URL.startsWith("http")) { 203 | Thread t = new Thread(new Runnable() { 204 | @Override 205 | public void run() { 206 | try { 207 | feed = new RssFeed_SAXParser().getFeed(RSS_URL); 208 | } catch (ParserConfigurationException e) { 209 | e.printStackTrace(); 210 | } catch (SAXException e) { 211 | e.printStackTrace(); 212 | } catch (IOException e) { 213 | e.printStackTrace(); 214 | } 215 | } 216 | }); 217 | t.start(); 218 | try { 219 | t.join(); 220 | } catch (InterruptedException e) { 221 | e.printStackTrace(); 222 | } 223 | Log.i("find", " "+findUrl(RSS_URL)); 224 | Log.i("find", " "+RSS_URL); 225 | feed.modify(findUrl(RSS_URL)); 226 | } 227 | else if (RSS_URL.equals("favor")){ // 收藏 228 | feed = new RssFeed(); 229 | File[] favors = new File(prefix+"/favor").listFiles(); 230 | for (int i = 0; i < favors.length; ++i) { 231 | System.out.println(favors[i].getPath()); 232 | feed.addItem(new RssItem(readFile(favors[i].getPath()))); 233 | } 234 | } 235 | else { // 推荐 236 | getBest(); 237 | Thread t = new Thread(new Runnable() { 238 | @Override 239 | public void run() { 240 | try { 241 | feed = new RssFeed_SAXParser().getFeed(RSS_URL); 242 | } catch (ParserConfigurationException e) { 243 | e.printStackTrace(); 244 | } catch (SAXException e) { 245 | e.printStackTrace(); 246 | } catch (IOException e) { 247 | e.printStackTrace(); 248 | } 249 | } 250 | }); 251 | t.start(); 252 | try { 253 | t.join(); 254 | } catch (InterruptedException e) { 255 | e.printStackTrace(); 256 | } 257 | feed.modify(findUrl(RSS_URL)); 258 | //----- 259 | t = new Thread(new Runnable() { 260 | @Override 261 | public void run() { 262 | try { 263 | feed2 = new RssFeed_SAXParser().getFeed(RSS_URL2); 264 | } catch (ParserConfigurationException e) { 265 | e.printStackTrace(); 266 | } catch (SAXException e) { 267 | e.printStackTrace(); 268 | } catch (IOException e) { 269 | e.printStackTrace(); 270 | } 271 | } 272 | }); 273 | t.start(); 274 | try { 275 | t.join(); 276 | } catch (InterruptedException e) { 277 | e.printStackTrace(); 278 | } 279 | feed2.modify(findUrl(RSS_URL2)); 280 | feed = merge(feed, feed2); 281 | RSS_URL = "recommend"; 282 | } 283 | showListView(); 284 | } 285 | private RssFeed merge(RssFeed f1, RssFeed f2) { 286 | feed = new RssFeed(); 287 | f1.refresh(); 288 | f2.refresh(); 289 | f1.getAllItems(getFilesDir().toString()); 290 | f2.getAllItems(getFilesDir().toString()); 291 | // 7:3 292 | for (int i = 0; i < f1.getSize() && feed.getSize() < 7; ++i) 293 | if (!f1.getItem(i).getTitle().contains("[已读] ")) 294 | feed.addItem(f1.getItem(i)); 295 | for (int i = 0; i < f2.getSize() && feed.getSize() < 10; ++i) 296 | if (!f2.getItem(i).getTitle().contains("[已读] ")) 297 | feed.addItem(f2.getItem(i)); 298 | return feed; 299 | } 300 | private String readFile(String path) { 301 | StringBuilder sb = new StringBuilder(); 302 | String s = ""; 303 | try { 304 | BufferedReader br = new BufferedReader(new FileReader(path)); 305 | while( (s = br.readLine()) != null) { 306 | sb.append(s + "\n"); 307 | } 308 | br.close(); 309 | } catch (IOException e) { 310 | e.printStackTrace(); 311 | } 312 | return sb.toString(); 313 | } 314 | public void showMultiChoiceDialog(final Context context) { 315 | final ArrayList yourChoices = new ArrayList<>(); 316 | final String[] items = { "国内新闻","国际新闻","经济新闻","体育新闻","台湾新闻","教育新闻","文化新闻","游戏新闻" }; 317 | // 设置默认选中的选项,全为false默认均未选中 318 | // yourChoices.clear(); 319 | // for (int i = 0; i < items.length; ++i) 320 | // if (choiceSets[i]) 321 | // yourChoices.add(i); 322 | // System.out.println(yourChoices); 323 | android.support.v7.app.AlertDialog.Builder multiChoiceDialog = 324 | new android.support.v7.app.AlertDialog.Builder(context); 325 | multiChoiceDialog.setTitle("请选择要订阅的新闻类型"); 326 | multiChoiceDialog.setMultiChoiceItems(items, choiceSets, 327 | new DialogInterface.OnMultiChoiceClickListener() { 328 | @Override 329 | public void onClick(DialogInterface dialog, int which, 330 | boolean isChecked) { 331 | Log.i("choice", " "+which); 332 | if (isChecked) { 333 | choiceSets[which] = true; 334 | // yourChoices.add(which); 335 | } else { 336 | choiceSets[which] = false; 337 | } 338 | } 339 | }); 340 | multiChoiceDialog.setPositiveButton("确定", 341 | new DialogInterface.OnClickListener() { 342 | @Override 343 | public void onClick(DialogInterface dialog, int which) { 344 | // int size = yourChoices.size(); 345 | // boolean[] choice = {false,false,false,false,false,false,false,false}; 346 | String str = ""; 347 | String file = ""; 348 | for (int i = 0; i < choiceSets.length; ++i) { 349 | if (choiceSets[i]) { 350 | str += items[i] + " "; 351 | file += "1"; 352 | } 353 | else 354 | file += "0"; 355 | } 356 | Toast.makeText(context, 357 | "你选中了:" + str, 358 | Toast.LENGTH_SHORT).show(); 359 | try { 360 | FileOutputStream out = context.openFileOutput("9bf6d3581229", MODE_PRIVATE); 361 | out.write(file.getBytes()); 362 | out.close(); 363 | } catch (FileNotFoundException e) { 364 | e.printStackTrace(); 365 | } catch (IOException e) { 366 | e.printStackTrace(); 367 | } 368 | setState(file); 369 | } 370 | }); 371 | multiChoiceDialog.show(); 372 | } 373 | private void setPullToRefesh(){ 374 | final PullToRefreshLayout pullToRefreshLayout = (PullToRefreshLayout)findViewById(R.id.id_lv_up); 375 | pullToRefreshLayout.setRefreshListener(new BaseRefreshListener() { 376 | @Override 377 | public void refresh() { 378 | new Handler().postDelayed(new Runnable() { 379 | @Override 380 | public void run() { 381 | String s = "没有更多新闻了哟~"; 382 | if (RSS_URL.equals("recommend")) 383 | s = "又推荐了好多呢~"; 384 | refreshMain(); 385 | Toast.makeText(context, s, Toast.LENGTH_SHORT).show(); 386 | // feed.refresh(); 387 | // 结束刷新 388 | pullToRefreshLayout.finishRefresh(); 389 | } 390 | }, 1000); 391 | 392 | } 393 | 394 | @Override 395 | public void loadMore() { 396 | new Handler().postDelayed(new Runnable() { 397 | 398 | @Override 399 | public void run() { 400 | if (RSS_URL.equals("recommend")) { 401 | refreshMain(); 402 | Toast.makeText(context, "又推荐了好多呢~", Toast.LENGTH_SHORT).show(); 403 | pullToRefreshLayout.finishLoadMore(); 404 | } 405 | else { 406 | feed.refresh(); 407 | simpleAdapter = new SimpleAdapter(context, feed.getAllItems(getFilesDir().toString()), android.R.layout.simple_list_item_2, new String[]{RssItem.TITLE, RssItem.PUBDATE,}, new int[]{android.R.id.text1, android.R.id.text2}); 408 | newsList.setAdapter(simpleAdapter); 409 | newsList.setSelection(newsList.getHeaderViewsCount()); 410 | // noMoreFlag = true; 411 | // 结束刷新 412 | pullToRefreshLayout.finishLoadMore(); 413 | Toast.makeText(context, "又加载了好多呢~", Toast.LENGTH_SHORT).show(); 414 | } 415 | } 416 | }, 1000); 417 | } 418 | }); 419 | } 420 | @Override 421 | public boolean onCreateOptionsMenu(Menu menu) { 422 | // Inflate the menu; this adds items to the action bar if it is present. 423 | getMenuInflater().inflate(R.menu.main, menu); 424 | return true; 425 | } 426 | @Override 427 | public boolean onOptionsItemSelected(MenuItem item) { 428 | // Handle action bar item clicks here. The action bar will 429 | // automatically handle clicks on the Home/Up button, so long 430 | // as you specify a parent activity in AndroidManifest.xml. 431 | int id = item.getItemId(); 432 | 433 | //noinspection SimplifiableIfStatement 434 | if (id == R.id.action_share) { 435 | showMultiChoiceDialog(context); 436 | // Toast.makeText(context, "要分享哪个新闻呢?", Toast.LENGTH_SHORT).show(); 437 | return true; 438 | } 439 | if (id == R.id.action_search) { 440 | searchContent(); 441 | return true; 442 | } 443 | if (id == R.id.action_nofavor) { 444 | File[] favors = new File(prefix+"/favor").listFiles(); 445 | for (int i = 0; i < favors.length; ++i) { 446 | new File(favors[i].getPath()).delete(); 447 | } 448 | Toast.makeText(context, "成功清空收藏!", Toast.LENGTH_SHORT).show(); 449 | return true; 450 | } 451 | if (id == R.id.action_clear) { 452 | Toast.makeText(context, "缓存清空成功!", Toast.LENGTH_SHORT).show(); 453 | return true; 454 | } 455 | 456 | return super.onOptionsItemSelected(item); 457 | } 458 | private void searchContent() { 459 | final EditText editText = new EditText(MainActivity.this); 460 | AlertDialog.Builder inputDialog = 461 | new AlertDialog.Builder(MainActivity.this); 462 | inputDialog.setTitle("在该页面搜索:").setView(editText); 463 | inputDialog.setPositiveButton("确定", 464 | new DialogInterface.OnClickListener() { 465 | @Override 466 | public void onClick(DialogInterface dialog, int which) { 467 | String s = editText.getText().toString(); 468 | feed.setSearch(s); 469 | showListView(); 470 | Toast.makeText(context,"已为您搜索关键字\""+s+"\"\n上拉即可恢复", Toast.LENGTH_SHORT); 471 | // feed.setSearch(""); 472 | } 473 | }).show(); 474 | } 475 | /* 476 | * 把RSS内容绑定到ui界面进行显示 477 | */ 478 | private void setUpColumn(int id, final String url) { 479 | TextView tv = (TextView) findViewById(id); 480 | final String s = tv.getText().toString(); 481 | tv.setOnClickListener(new View.OnClickListener() { 482 | @Override 483 | public void onClick(View v) { 484 | Log.i("click!", url); 485 | // Toast.makeText(context,"获取"+s+"中", Toast.LENGTH_SHORT).show(); 486 | RSS_URL = url; 487 | refreshMain(); 488 | Toast.makeText(context, s + "切换成功!\n向右滑动返回", Toast.LENGTH_SHORT).show(); 489 | // Toast.makeText(context, s + "切换成功!\n向右滑动返回", Toast.LENGTH_SHORT).show(); 490 | } 491 | }); 492 | } 493 | private void showListView() { 494 | Log.i(tag,"success"); 495 | newsList = (ListView) this.findViewById(R.id.list); 496 | if (!NetworkAvail.check(this)) { 497 | ShowDescriptionActivity.show("没有网络qwq请检查", this, true); 498 | Log.i("network", "no"); 499 | return; 500 | } 501 | else 502 | Log.i("network", "yes "); 503 | if (feed == null) { 504 | ShowDescriptionActivity.show("访问的RSS无效", this, true); 505 | return; 506 | } 507 | 508 | simpleAdapter = new SimpleAdapter(this, 509 | feed.getAllItems(getFilesDir().toString()), android.R.layout.simple_list_item_2, 510 | new String[] { RssItem.TITLE, RssItem.PUBDATE, }, new int[] { 511 | android.R.id.text1, android.R.id.text2 }); 512 | newsList.setAdapter(simpleAdapter); 513 | newsList.setOnItemClickListener(this); 514 | newsList.setSelection(0); 515 | } 516 | 517 | @Override 518 | public void onItemClick(AdapterView adapterView, View view, int position, long id) { 519 | position = feed.getmap(position); 520 | String s = feed.getItem(position).getTitle(); 521 | 522 | Intent intent = new Intent(); 523 | intent.setClass(this, ItemNewsActivity.class); 524 | intent.putExtra("link", feed.getItem(position).getLink()); 525 | intent.putExtra("file", feed.getItem(position).toFileName()); 526 | intent.putExtra("info", feed.getItem(position).toFavor()); 527 | Bundle bundle = new Bundle(); 528 | bundle.putString("title", feed.getItem(position).getTitle()); 529 | bundle.putString("description",feed.getItem(position).getDescription()); 530 | bundle.putString("link", feed.getItem(position).getLink()); 531 | bundle.putString("pubdate", feed.getItem(position).getPubdate()); 532 | // 用android.intent.extra.INTENT的名字来传递参数 533 | intent.putExtra("android.intent.extra.rssItem", bundle); 534 | startActivity(intent); 535 | int first = newsList.getFirstVisiblePosition(); 536 | if (!s.startsWith("[已读] ")) { 537 | feed.getItem(position).setTitle("[已读] " + s); 538 | ++count[Integer.parseInt(feed.getItem(position).getCategory())]; 539 | logCount(); 540 | simpleAdapter = new SimpleAdapter(this, 541 | feed.getAllItems(getFilesDir().toString()), android.R.layout.simple_list_item_2, 542 | new String[] { RssItem.TITLE, RssItem.PUBDATE, }, new int[] { 543 | android.R.id.text1, android.R.id.text2 }); 544 | // newsList.getChildAt(position-first).setBackgroundColor(getResources().getColor(R.color.colorAccent)); 545 | newsList.setAdapter(simpleAdapter); 546 | newsList.setSelection(first); 547 | } 548 | } 549 | private void logCount() { 550 | String s = ""; 551 | for (int i = 0; i < count.length; ++i) 552 | s += count[i] + " "; 553 | try { 554 | FileWriter fw = new FileWriter(prefix+"/rec.txt"); 555 | fw.write(s); 556 | fw.close(); 557 | } catch (FileNotFoundException e) { 558 | e.printStackTrace(); 559 | } catch (IOException e) { 560 | e.printStackTrace(); 561 | } 562 | } 563 | } 564 | -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/MyWebViewClient.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi; 2 | 3 | import android.util.Log; 4 | import android.webkit.WebView; 5 | import android.webkit.WebViewClient; 6 | 7 | public class MyWebViewClient extends WebViewClient { 8 | String baseUrl; 9 | MyWebViewClient(String url) { baseUrl = url; } 10 | @Override 11 | public boolean shouldOverrideUrlLoading(WebView view, String url) { //网页加载时的连接的网址 12 | if (!url.startsWith("http:") ) { 13 | view.loadUrl(baseUrl+url); 14 | Log.i("imgurl", baseUrl+url); 15 | return false; 16 | } 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/NetworkAvail.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | public class NetworkAvail { 7 | /** 8 | * 检查网络是否可用 9 | * 10 | * @param context 11 | * @return 12 | */ 13 | public static boolean check(Context context) { 14 | 15 | ConnectivityManager manager = (ConnectivityManager) context 16 | .getApplicationContext().getSystemService( 17 | Context.CONNECTIVITY_SERVICE); 18 | 19 | if (manager == null) { 20 | return false; 21 | } 22 | 23 | NetworkInfo networkinfo = manager.getActiveNetworkInfo(); 24 | 25 | if (networkinfo == null || !networkinfo.isAvailable()) { 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/ShowDescriptionActivity.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.os.Bundle; 6 | import android.app.Activity; 7 | import android.content.Intent; 8 | import android.support.v7.app.AlertDialog; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.View.OnClickListener; 13 | import android.widget.Button; 14 | import android.widget.TextView; 15 | import android.support.v4.app.NavUtils; 16 | 17 | public class ShowDescriptionActivity { 18 | public static void show(String e, Context c, final boolean exit) { 19 | AlertDialog.Builder d = new AlertDialog.Builder(c); 20 | d.setTitle(e); 21 | d.setPositiveButton("确定", 22 | new DialogInterface.OnClickListener() { 23 | @Override 24 | public void onClick(DialogInterface dialog, int which) { 25 | if (exit) 26 | System.exit(0); 27 | } 28 | }); 29 | d.show(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/WebContent.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi; 2 | import java.io.BufferedWriter; 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.PrintWriter; 7 | import java.net.MalformedURLException; 8 | import java.net.URL; 9 | import java.util.Scanner; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | public class WebContent { 14 | 15 | /** 16 | * 返回当前URL的网页HTML代码 17 | * 18 | * @param htmlURL 19 | * - 网页地址 20 | * @return 当前网页的HTML代码 21 | */ 22 | static public String getHtml(String htmlURL) { 23 | URL url; 24 | Scanner in = null; 25 | StringBuilder sb = new StringBuilder(); 26 | String charset; 27 | System.out.println(htmlURL); 28 | try { 29 | url = new URL(htmlURL); 30 | charset = getCharset(htmlURL); 31 | System.out.println(charset); 32 | in = new Scanner(new InputStreamReader(url.openStream(), charset)); 33 | while (in.hasNextLine()) { 34 | sb.append(in.nextLine()); 35 | } 36 | return sb.toString(); 37 | } catch (MalformedURLException e) { 38 | System.out.println("URL错误!!"+sb.toString()); 39 | throw new ExceptionInInitializerError(e); 40 | } catch (IOException e) { 41 | System.out.println("字符串处理出错!!"+sb.toString()); 42 | throw new ExceptionInInitializerError(e); 43 | } finally { 44 | if (in != null) { 45 | in.close(); 46 | } 47 | } 48 | 49 | } 50 | 51 | /** 52 | * 通过URL取得一个网页的编码集 53 | * 54 | * @param htmlURL 55 | * - 网页URL 56 | * @return 编码集名称
57 | * 如果未找到则返回 空字符串 58 | */ 59 | static public String getCharset(final String htmlURL) { 60 | Scanner in = null; 61 | try { 62 | URL url = new URL(htmlURL); 63 | in = new Scanner(url.openStream()); 64 | StringBuilder sb = new StringBuilder(); 65 | String regex = "charset=[^>\\s]*\\b"; 66 | Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); 67 | Matcher m; 68 | while (in.hasNextLine()) { 69 | sb.append(in.nextLine()); 70 | m = p.matcher(sb.toString()); 71 | if (m.find()) { 72 | return m.group().toLowerCase() 73 | .replaceAll("charset=\"?", "").trim(); 74 | } 75 | } 76 | return ""; 77 | } catch (MalformedURLException e) { 78 | return "gb2312"; 79 | // System.out.println("URL错误!!"); 80 | // throw new ExceptionInInitializerError(e); 81 | } catch (IOException e) { 82 | return "gb2312"; 83 | // System.out.println("字符串处理出错!!"); 84 | // throw new ExceptionInInitializerError(e); 85 | } finally { 86 | if (in != null) { 87 | in.close(); 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * 从网页HTML代码中去的文章标题 94 | * 95 | * @param html 96 | * - 网页HTML代码 97 | * @return 文章标题
98 | * 如果未找到则返回 空字符串 99 | */ 100 | static public String getTitle(final String html) { 101 | String regex = "[\\s\\S]*?"; 102 | Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); 103 | Matcher m = p.matcher(html); 104 | if (m.find()) { 105 | return m.group().toLowerCase().replaceAll("", "").trim(); 106 | } else { 107 | return ""; 108 | } 109 | } 110 | 111 | public static void write(String htmlURL) throws IOException { 112 | // String htmlURL = "http://www.baidu.com"; 113 | // String htmlURL = "http://www.tangshanqiche.com/nNews/ShowNews.aspx?PageNumber=2&NewsClassTypeId=105"; 114 | String html = getHtml(htmlURL); 115 | // String title = getTitle(html); 116 | 117 | // PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter( 118 | // getFilesDir().toString() + "/" + htmlURL.replace("/", "_")))); 119 | // out.write(html); 120 | // out.close(); 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/rss/RssFeed.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi.rss; 2 | 3 | import android.util.Log; 4 | 5 | import com.java.wengjiayi.MainActivity; 6 | 7 | import java.io.File; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | 13 | public class RssFeed { 14 | 15 | private String title; // 标题 16 | private String pubdate; // 发布日期 17 | private int itemCount; // 用于计算列表的数目 18 | private List rssItems; // 用于描述列表item 19 | private String search; 20 | public List> data; 21 | public ArrayList mapping; 22 | public RssFeed() { 23 | itemCount = 0; 24 | search = ""; 25 | mapping = new ArrayList(); 26 | rssItems = new ArrayList(); 27 | data = new ArrayList>(); 28 | } 29 | public int getSize() { return rssItems.size(); } 30 | // 添加RssItem条目,返回列表长度 31 | public int addItem(RssItem rssItem) { 32 | rssItems.add(rssItem); 33 | itemCount++; 34 | return itemCount; 35 | } 36 | public void modify(int id) { 37 | Log.i("modify",id+" "); 38 | Log.i("modify",rssItems.size()+" "); 39 | for (int i = 0; i < rssItems.size(); ++i) 40 | rssItems.get(i).setCategory(Integer.toString(id)); 41 | } 42 | // 根据下标获取RssItem 43 | public RssItem getItem(int position) { 44 | return rssItems.get(position); 45 | } 46 | public void clearSearch() { search = ""; } 47 | public void setSearch(String s) { search = s; } 48 | public void refresh() { 49 | Collections.shuffle(rssItems); 50 | } 51 | public int getmap(int pos) { return mapping.get(pos); } 52 | public List> getAllItems(String path) { 53 | data.clear(); 54 | mapping.clear(); 55 | for (int i = 0; i < rssItems.size(); i++) { 56 | File file = new File(path+"/"+rssItems.get(i).getLink().replace("/", "_").replace(":", "")); 57 | if (file.exists() && !rssItems.get(i).getTitle().startsWith("[已读] ")) 58 | rssItems.get(i).setTitle("[已读] " + rssItems.get(i).getAllTitle()); 59 | String desc = rssItems.get(i).getDescription(); 60 | String title = rssItems.get(i).getTitle(); 61 | if (search.length() == 0 || rssItems.get(i).getAllTitle().contains(search)) { 62 | mapping.add(i); 63 | HashMap item = new HashMap(); 64 | item.put(RssItem.TITLE, title); 65 | item.put(RssItem.PUBDATE, desc); 66 | data.add(item); 67 | } 68 | } 69 | return data; 70 | } 71 | 72 | public String getTitle() { 73 | return title; 74 | } 75 | 76 | public void setTitle(String title) { 77 | this.title = title; 78 | } 79 | 80 | public String getPubdate() { 81 | return pubdate; 82 | } 83 | 84 | public void setPubdate(String pubdate) { 85 | this.pubdate = pubdate; 86 | } 87 | 88 | public int getItemCount() { 89 | return itemCount; 90 | } 91 | 92 | public void setItemCount(int itemCount) { 93 | this.itemCount = itemCount; 94 | } 95 | 96 | public ArrayList getLinks() { 97 | ArrayList links = new ArrayList(); 98 | for (int i = 0; i < rssItems.size(); ++i) 99 | links.add(rssItems.get(i).getLink()); 100 | return links; 101 | } 102 | } -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/rss/RssFeed_SAXParser.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi.rss; 2 | import java.io.*; 3 | import java.net.URL; 4 | 5 | import javax.xml.parsers.ParserConfigurationException; 6 | import javax.xml.parsers.SAXParser; 7 | import javax.xml.parsers.SAXParserFactory; 8 | import android.util.Log; 9 | 10 | import org.xml.sax.InputSource; 11 | import org.xml.sax.SAXException; 12 | import org.xml.sax.XMLReader; 13 | 14 | public class RssFeed_SAXParser { 15 | 16 | public RssFeed getFeed(String urlStr) throws ParserConfigurationException, SAXException, IOException{ 17 | URL url = new URL(urlStr); 18 | url.openStream(); 19 | SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); //构建SAX解析工厂 20 | SAXParser saxParser = saxParserFactory.newSAXParser(); //解析工厂生产解析器 21 | XMLReader xmlReader = saxParser.getXMLReader(); //通过saxParser构建xmlReader阅读器 22 | RssHandler rssHandler=new RssHandler(); 23 | xmlReader.setContentHandler(rssHandler); 24 | //使用url打开流,并将流作为 xmlReader解析的输入源并解析 25 | InputSource inputSource = new InputSource(url.openStream()); 26 | xmlReader.parse(inputSource); 27 | return rssHandler.getRssFeed(); 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/rss/RssHandler.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi.rss; 2 | 3 | import org.xml.sax.Attributes; 4 | import org.xml.sax.SAXException; 5 | import org.xml.sax.helpers.DefaultHandler; 6 | 7 | //import android.util.Log; 8 | 9 | public class RssHandler extends DefaultHandler { 10 | 11 | RssFeed rssFeed; 12 | RssItem rssItem; 13 | 14 | String lastElementName = "";// 标记变量,用于标记在解析过程中我们关心的几个标签,若不是我们关心的标签记做0 15 | 16 | final int RSS_TITLE = 1;// 若是 title 标签,记做1,注意有两个title,但我们都保存在item的成员变量中 17 | final int RSS_LINK = 2;// 若是 link 标签,记做2 18 | final int RSS_DESCRIPTION = 3;// 若是 description 标签,记做3 19 | final int RSS_CATEGORY = 4;// 若是category标签,记做 4 20 | final int RSS_PUBDATE = 5; // 若是pubdate标签,记做5,注意有两个pubdate,但我们都保存在item的pubdate成员变量中 21 | 22 | int currentFlag = 0; 23 | 24 | public RssHandler() { 25 | 26 | } 27 | 28 | @Override 29 | public void startDocument() throws SAXException { 30 | super.startDocument(); 31 | rssFeed = new RssFeed(); 32 | rssItem = new RssItem(); 33 | 34 | } 35 | 36 | @Override 37 | public void characters(char[] ch, int start, int length) 38 | throws SAXException { 39 | super.characters(ch, start, length); 40 | // 获取字符串 41 | String text = new String(ch, start, length); 42 | // Log.i("i", "要获取的内容:" + text); 43 | 44 | switch (currentFlag) { 45 | case RSS_TITLE: 46 | rssItem.setTitle(text); 47 | currentFlag = 0;// 设置完后,重置为开始状态 48 | break; 49 | case RSS_PUBDATE: 50 | rssItem.setPubdate(text); 51 | currentFlag = 0;// 设置完后,重置为开始状态 52 | break; 53 | case RSS_CATEGORY: 54 | rssItem.setCategory(text); 55 | currentFlag = 0;// 设置完后,重置为开始状态 56 | break; 57 | case RSS_LINK: 58 | rssItem.setLink(text); 59 | currentFlag = 0;// 设置完后,重置为开始状态 60 | break; 61 | case RSS_DESCRIPTION: 62 | rssItem.setDescription(text); 63 | currentFlag = 0;// 设置完后,重置为开始状态 64 | break; 65 | default: 66 | break; 67 | } 68 | } 69 | 70 | @Override 71 | public void startElement(String uri, String localName, String qName, 72 | Attributes attributes) throws SAXException { 73 | super.startElement(uri, localName, qName, attributes); 74 | if ("chanel".equals(localName)) { 75 | // 这个标签内没有我们关心的内容,所以不作处理,currentFlag=0 76 | currentFlag = 0; 77 | return; 78 | } 79 | if ("item".equals(localName)) { 80 | rssItem = new RssItem(); 81 | return; 82 | } 83 | if ("title".equals(localName)) { 84 | currentFlag = RSS_TITLE; 85 | return; 86 | } 87 | if ("description".equals(localName)) { 88 | currentFlag = RSS_DESCRIPTION; 89 | return; 90 | } 91 | if ("link".equals(localName)) { 92 | currentFlag = RSS_LINK; 93 | return; 94 | } 95 | if ("pubDate".equals(localName)) { 96 | currentFlag = RSS_PUBDATE; 97 | return; 98 | } 99 | if ("category".equals(localName)) { 100 | currentFlag = RSS_CATEGORY; 101 | return; 102 | } 103 | 104 | } 105 | 106 | @Override 107 | public void endElement(String uri, String localName, String qName) 108 | throws SAXException { 109 | super.endElement(uri, localName, qName); 110 | // 如果解析一个item节点结束,就将rssItem添加到rssFeed中。 111 | if ("item".equals(localName)) { 112 | 113 | rssFeed.addItem(rssItem); 114 | return; 115 | } 116 | } 117 | 118 | @Override 119 | public void endDocument() throws SAXException { 120 | super.endDocument(); 121 | } 122 | 123 | public RssFeed getRssFeed() { 124 | return rssFeed; 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /app/src/main/java/com/java/wengjiayi/rss/RssItem.java: -------------------------------------------------------------------------------- 1 | package com.java.wengjiayi.rss; 2 | 3 | 4 | import android.util.Log; 5 | 6 | import com.java.wengjiayi.MainActivity; 7 | 8 | import java.io.File; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | public class RssItem { 13 | 14 | private String title; 15 | private String description; 16 | private String link; 17 | private String category; 18 | private String pubdate; 19 | 20 | public static final String TITLE = "title"; 21 | public static final String PUBDATE = "pubdate"; 22 | public static final String DESC = "description"; 23 | 24 | public RssItem() { 25 | 26 | } 27 | public RssItem(String q) { 28 | String[] items = q.split("\n"); 29 | title = items[0]; 30 | description = items[1]; 31 | link = items[2]; 32 | category = items[3]; 33 | pubdate = items[4]; 34 | } 35 | public String getAllTitle() { return title; } 36 | public String getTitle() { 37 | // System.out.println(title); 38 | if (title.length() > 20) { 39 | return title.substring(0, 19) + "..."; 40 | } 41 | return title; 42 | } 43 | 44 | public void setTitle(String title) { this.title = title; } 45 | 46 | public String getDescription() { 47 | if (description == null) 48 | return "[ 点击阅读更多 ]"; 49 | // Log.i("length"," "+description.length()); 50 | if (description.length() > 23) { 51 | return description.substring(0, 22) + "..."; 52 | } 53 | return description; 54 | } 55 | 56 | public void setDescription(String description) { 57 | description += " > [ 点击阅读更多 ]"; 58 | description = description.replaceAll("<(.*?)>", " ").replaceAll(">", ">").replaceAll("<", "<").replaceAll(" ", "").replaceAll(" {2,}", " ").replaceAll(" {2,}", " ") ; 59 | this.description = description; 60 | // Log.i("desc", description); 61 | } 62 | 63 | public String getLink() { 64 | return link; 65 | } 66 | 67 | public void setLink(String link) { 68 | this.link = link; 69 | } 70 | 71 | public String getCategory() { 72 | return category; 73 | } 74 | 75 | public void setCategory(String category) { 76 | this.category = category; 77 | } 78 | 79 | public String getPubdate() { 80 | return pubdate; 81 | } 82 | 83 | public void setPubdate(String pubdate) { 84 | this.pubdate = pubdate; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return "RssItem [title=" + title + ", description=" + description 90 | + ", link=" + link + ", category=" + category + ", pubdate=" 91 | + pubdate + "]"; 92 | } 93 | public String toFavor() { 94 | // if (tmp.length() > 50) { 95 | // tmp = tmp.substring(0, 49); 96 | // } 97 | String tmp = title + "\n" + description + "\n" + link + "\n" + category + "\n" + pubdate; 98 | // tmp = tmp.replaceAll(" ", "").replaceAll(" ", ""); 99 | Log.i("toFavor", " "+tmp.length()); 100 | return tmp; 101 | } 102 | public String toFileName() { 103 | return link.replace("/", "_").replace(":", ""); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_menu_manage.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_menu_send.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_menu_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/drawable/icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trinkle23897/simple-news-android-app/7c23211a9944e629b2650cfed33059ff5908be37/app/src/main/res/drawable/icon2.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_customize.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_itemnews.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 13 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_newstop_content.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_show_description.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 17 | 18 |